2011-12-09 27 views
2

我正在开发一个应用程序,在该应用程序中,我遍历表中的许多(1,000,000+)行,同时插入新行并更新现有行。需要select语句产生表中的每一行(最初执行select时存在的行)一次,并且绝不会产生在执行select后插入的行。我宁愿不将所有行加载到内存中(这需要很长时间和很多内存 - 我试过了)。SQLite:选择受后续插入影响的结果

我开发了一个小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

是否有更好的方法来解决这个比数据复制到一个单独的(临时)表,并从那里选择?

说明:我忽略说我需要在循环内部进行提交。该过程可能会中断,部分完成的工作必须提交,以便在下一个运行时不需要重做。

+0

你有没有发现这样做的一个很好的方式

  • 使用单独的连接?我偶然发现了同样的事情。 –

  • +0

    @StavrosKorokithakis我最终在文件中缓存了SELECT的结果。 WAL模式,Doug Currie在下面提到,也可能是一个可行的解决方案,但是对于我的特殊情况来说,权衡并不值得。 – millerdev

    +0

    啊,这听起来不是最理想的,但你能做什么...感谢您的帮助。 –

    回答

    5
    1. 使用WAL mode(使作者和读者不会干扰),为读者和作家
    +0

    我还没有听说过WAL模式。这很有趣。我会检查出来的。 – millerdev

    2

    如果添加在你SELECT结束打开你的递延交易模式和数据库COMMIT - 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语句结束之前,这些更改都不会提交。

    +0

    谢谢你的回应。虽然如果选择不那么庞大,这将是一个很好的方法,但我希望在此过程中进行提交,这样我就可以恢复过程,而不必在稍后发生程序崩溃时重做所有工作。 – millerdev