SQLite:选择受后续插入影响的结果

时间:2011-12-09 05:44:06

标签: python sqlite select insert isolation

我正在开发一个应用程序,我在其中迭代表中的许多(1,000,000+)行,同时插入新行并沿途更新现有行。要求select语句产生表中的每一行(最初执行select时存在的那一行)恰好一次,并且永远不会产生在执行select之后插入的行。我宁愿不将所有行加载到内存中(这需要很长时间和大量RAM - 我尝试过它。)

我开发了一个小的Python示例,该示例演示了SQLite显然不会从长时间运行的选择中隔离插入(并且可能是更新和删除)。我无法在SQLite文档中找到任何特别提到这种行为的地方,但是我发现了一些链接,这些链接暗示了插入失败的事实(可能在早期版本的SQLite中?),这在我的例子中没有

import sqlite3

def select_affected_by_insert():
    # select from and simultaneously modify same table
    cn = sqlite3.connect(':memory:')
    cn.execute("CREATE TABLE demo (v INTEGER PRIMARY KEY)")

    n = 5
    values = [[v] for v in range(n)]
    cn.executemany('INSERT INTO demo VALUES (?)', values)

    for (v,) in cn.execute('SELECT v FROM demo'):

        with cn:
            # insert in transaction
            cn.execute('INSERT INTO demo VALUES (?)', [n + v])

        print v, n + v
        assert v < n, 'got more rows than expected!'

if __name__ == '__main__':
    select_affected_by_insert()

SQLite 3.6.12
Python 2.6.4

有没有更好的方法来解决这个问题,而不是将数据复制到单独的(临时)表并从那里进行选择?

澄清:我忽略了说我需要在循环中做提交。该过程可能会中断,并且必须提交部分完成的工作,因此不需要在下一次运行时重做。

2 个答案:

答案 0 :(得分:5)

  1. 使用WAL mode(因此作者和读者不会干涉)
  2. 为读写器使用单独的连接

答案 1 :(得分:2)

如果您以延迟交易模式添加数据库,并在COMMIT - SELECT逻辑结束时添加INSERT,如下所示:

cn = sqlite3.connect(':memory:', isolation_level='DEFERRED')
...
for (v,) in cn.execute('SELECT v FROM demo'):
    cn.execute('INSERT INTO demo VALUES (?)', [n + v])
cn.commit()

您的插入语句应推迟到块结束。来自SQLite Docs for Transaction Control

  

如果同时针对同一SQLite数据库连接执行多个命令,则自动提交将推迟到最后一个命令完成。例如,如果正在执行SELECT语句,则在返回结果的每一行时,命令的执行将暂停。在此暂停期间,可以对数据库中的其他表执行其他INSERT,UPDATE或DELETE命令。 但在原始SELECT语句完成之前,这些更改都不会提交。