这个Python代码创建一个表,在其中插入三行并遍历行,并在光标完全耗尽之前进行干预提交。为什么它返回五行而不是三行?如果删除介入提交,则返回的行数按预期为三。或者是否期望提交(甚至不触及相关表)会使游标无效?
修改:添加了一个忘记的提交(使问题消失)和插入到不相关的表(这会使问题再次出现)。
#!/usr/bin/env python3
import sqlite3 as sq
db = sq.connect(':memory:')
db.execute('CREATE TABLE tbl (col INTEGER)')
db.execute('CREATE TABLE tbl2 (col INTEGER)')
db.executemany('INSERT INTO tbl (col) VALUES (?)', [(0,), (1,), (2,)])
db.commit()
print('count=' + str(db.execute('SELECT count(*) FROM tbl').fetchone()[0]))
# Read and print the values just inserted into tbl
for col in db.execute('SELECT col FROM tbl'):
print(col)
db.execute('INSERT INTO tbl2 VALUES (?)', col)
db.commit()
print('count=' + str(db.execute('SELECT count(*) FROM tbl').fetchone()[0]))
输出结果为:
count=3
(0,)
(1,)
(0,)
(1,)
(2,)
count=3
通常,插入N行后,迭代器返回N + 2行,显然总是前两行重复。
答案 0 :(得分:4)
你的后续评论让我感到不安(特别是因为很明显你是对的)。所以我花了一些时间研究python _sqlite.c库(https://svn.python.org/projects/python/trunk/Modules/_sqlite/)的源代码。
我认为问题是sqlite Connection
对象如何处理游标。在内部,Connection
对象维护游标和预准备语句的列表。嵌套的db.execute('INSERT ...')
调用将重置与Connection
对象关联的预准备语句列表。
解决方案是不依赖于快捷方式execute()方法的自动游标管理,并明确保存对正在运行的Cursor
的引用。 Cursors
维护自己准备好的语句列表,这些列表与Connection
个对象分开。
您可以在db.execute()调用上显式创建游标或调用fetchall()。后来的例子:
import sqlite3 as sq
db = sq.connect(':memory:')
db.execute('CREATE TABLE tbl (col INTEGER)')
db.execute('CREATE TABLE tbl2 (col INTEGER)')
db.executemany('INSERT INTO tbl (col) VALUES (?)', [(0,), (1,), (2,)])
db.commit()
print('count=' + str(db.execute('SELECT count(*) FROM tbl').fetchone()[0]))
# Read and print the values just inserted into tbl
for col in db.execute('SELECT col FROM tbl').fetchall():
print(col)
db.execute('INSERT INTO tbl2 VALUES (?)', col)
db.commit()
print('count=' + str(db.execute('SELECT count(*) FROM tbl').fetchone()[0]))
输出符合预期:
count=3
(0,)
(1,)
(2,)
count=3
如果fetchall()
方法内存不允许,那么您可能需要依赖于两个数据库连接(https://www.sqlite.org/isolation.html)之间的隔离。例如:
db1 = sq.connect('temp.db')
db1.execute('CREATE TABLE tbl (col INTEGER)')
db1.execute('CREATE TABLE tbl2 (col INTEGER)')
db1.executemany('INSERT INTO tbl (col) VALUES (?)', [(0,), (1,), (2,)])
db1.commit()
print('count=' + str(db1.execute('SELECT count(*) FROM tbl').fetchone()[0]))
db2 = sq.connect('temp.db')
# Read and print the values just inserted into tbl
for col in db1.execute('SELECT col FROM tbl').fetchall():
print(col)
db2.execute('INSERT INTO tbl2 VALUES (?)', col)
db2.commit()
print('count=' + str(db1.execute('SELECT count(*) FROM tbl').fetchone()[0]))