当前位置:首页 > 数据库 > SQlite

Python,SQLite3:在提交干预后,游标返回重复项

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行,显然总是重复前两行.

解决方法:

您的后续评论打扰了我(特别是因为很明显您是对的).因此,我花了一些时间研究python _sqlite.c库(https://svn.python.org/projects/python/trunk/Modules/_sqlite/)的源代码.

我认为问题是sqlite Connection对象如何处理游标.在内部,Connection对象维护游标和准备好的语句的列表.嵌套的db.execute(‘INSERT …’)调用将重置与Connection对象关联的准备好的语句列表.

解决方案是不依赖快捷方式execute()方法的自动游标管理,而是显式保存对正在运行的Cursor的引用.游标维护自己准备好的语句列表,这些语句列表与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]))

【说明】本文章由站长整理发布,文章内容不代表本站观点,如文中有侵权行为,请与本站客服联系(QQ:254677821)!