【问题标题】:sqlite cursor inconsistent behaviour when altering underlying database更改底层数据库时,sqlite 游标不一致的行为
【发布时间】:2019-03-01 14:20:55
【问题描述】:

我无法理解 python 的 sqlite3 模块的光标对象的行为。据我了解,游标对象的行为类似于在数据结构上保持“视图”的迭代器。现在,我认为这有两种表现方式:

1) 在执行时,与SELECT * 语句匹配的数据库状态与期货更改保持分离

2) 在执行时,与SELECT * 语句匹配的数据库状态只是底层可变数据的视图。 IE。执行 for ... in cur 行后,就会进行惰性求值。

但显然情况并非如此,请参阅下面的脚本和输出。执行这个脚本时,为什么UPDATE命令没有并入游标,而INSERT是?总是使用result = list(cur.execute(...)) 会更好吗?

#!/usr/bin/env python3
import sqlite3

con = sqlite3.connect("db.sqlite")
con.execute("""CREATE TABLE IF NOT EXISTS `table` (
    `id`    INTEGER UNIQUE,
    `name`  TEXT,
    PRIMARY KEY(`id`)
);""")
con.execute("INSERT INTO `table` VALUES (1, 'smith')")
con.execute("INSERT INTO `table` VALUES (2, 'mia')")
con.commit()
print("in db: (1, smith), (2, mia)")

### Querying the table
cur = con.cursor()
cur.execute("SELECT * FROM `table`")

### Changing the table
print("altering table: add (3, kim), change (1, smith) to (1, james)")
con.execute("UPDATE `table` SET name='james' where id=1")
con.execute("INSERT INTO `table` VALUES (3, 'kim')")
con.commit()

print()
print("1) expect immutable: (1, smith), (2, mia)")
print("2) expect mutable: (1, james), (2, mia), (3, kim)")
print()
print("But got: ")
for row in cur: print(row)

输出

in db: (1, smith), (2, mia)
altering table: add (3, kim), change (1, smith) to (1, james)

1) expect immutable: (1, smith), (2, mia)
2) expect mutable: (1, james), (2, mia), (3, kim)

But got: 
(1, 'smith')
(2, 'mia')
(3, 'kim')

平台

  • Ubuntu 18.10 x64
  • Python 3.6.7(默认,2018 年 10 月 22 日,11:32:17)
  • sqlite3.version = '2.6.0'

【问题讨论】:

  • 我明白了:(1, u'james') (2, u'mia') (3, u'kim')。我在 Python 2.7 windows 7 上
  • 真的吗?奇怪的是,我刚刚再次检查了我提供的源代码。徒劳无功。你用的是什么平台?我在 python3.6.7 ubuntu 18.10

标签: python sqlite database-cursor


【解决方案1】:

不幸的是,这是 SQLite 的工作方式。来自Isolation In SQLite

...但是当 SELECT 语句运行时发生的变化呢?如果一个 SELECT 语句被启动并且 sqlite3_step() 接口通过大约一半的输出,然后一些 UPDATE 语句由应用程序运行,修改 SELECT 语句正在读取的表,然后对 sqlite3_step() 进行更多调用。完成 SELECT 语句? SELECT 语句的后续步骤是否会看到 UPDATE 所做的更改?答案是这种行为是未定义的......因此,开发人员应该努力避免编写对在这种情况下会发生什么做出假设的应用程序。

这意味着 SQLite 可以在到同一个数据库的不同连接之间提供一些隔离(注意,只有一个应该写入),但是你不应该尝试修改数据库,至少正在读取的表,在开始之间以及 SELECT 的结尾。

【讨论】:

  • 太。我不太明白。我只有一个连接,尽管有两个不同的游标(afaik,connection.execute 临时创建一个)。所以解决这个问题最省钱的方法就是对所有事情都使用 list(..) 吗? connection.commit() 呢?这不是同步障碍吗?
  • 呃,我想这是我在 URL 中引用的问题:同一数据库连接上的操作之间没有隔离。所以两个连接可以解决这个问题,而不是使用两个游标?
  • @JonasB。是的。它在链接页面中是明确的(我只复制了我认为答案中最相关的部分)。如果您有两个不同的连接,一个用于选择,而在选择期间另一个用于插入或更新,则选择中不会看到任何修改。
  • 我在使用两个连接时遇到了锁定问题。一旦我从一个连接调用cur.execute(...),数据库就会被锁定,直到我遍历结果。我不能打电话给con2.commit(),因为它超时了。我想使用 list(...) 是最简单的选择。我觉得我在这里做错了什么。
  • @JonasB。虽然一次可以从 sqlite 数据库读取任意数量的连接,但一次只能写入一个,并且在发生这种情况时什么都无法读取(除非使用 WAL mode)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-26
  • 2015-12-26
  • 2018-06-06
  • 2017-05-01
  • 1970-01-01
相关资源
最近更新 更多