3

我正在开发一个应用程序,我在其中迭代表中的许多(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

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

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

4

2 回答 2

5
  1. 使用WAL 模式(所以作者和读者不干涉)
  2. 为读取器和写入器使用单独的连接
于 2011-12-09T18:28:37.737 回答
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 语句完成之前,这些更改都不会提交。

于 2011-12-09T05:57:14.173 回答