3

新しい行を挿入し、途中で既存の行を更新しながら、テーブル内の多くの (1,000,000 +) 行を反復処理するアプリケーションを開発しています。select ステートメントは、テーブル内のすべての行 (select が最初に実行されたときに存在する行) を 1 回だけ生成し、select の実行後に挿入された行を決して生成しないことが要件です。すべての行をメモリにロードするのは避けたいと思います (これには長い時間と大量の RAM が必要です。試してみました)。

私は、SQLite が実行時間の長い選択から挿入 (およびおそらく更新と削除) を分離しないように見えることを示す小さな Python の例を開発しました。この動作について具体的に言及している 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
パイソン 2.6.4

データを別の (一時) テーブルにコピーしてそこから選択するよりも、これを回避するより良い方法はありますか?

明確化: ループ内でコミットを行う必要があることを言い忘れていました。プロセスは中断される可能性があり、部分的に完了した作業はコミットする必要があるため、次の実行時にやり直す必要はありません。

4

2 に答える 2

5
  1. WAL モードを使用する(ライターとリーダーが干渉しないようにするため)
  2. リーダーとライターに別々の接続を使用する
于 2011-12-09T18:28:37.737 に答える
2

次のように、データベースを遅延トランザクション モードでオープンし、ロジックの最後に追加すると、COMMIT次のようになります。SELECTINSERT

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 ドキュメントから:

複数のコマンドが同じ SQLite データベース接続に対して同時に実行されている場合、自動コミットは最後のコマンドが完了するまで延期されます。たとえば、SELECT ステートメントが実行されている場合、コマンドの実行は、結果の各行が返されるたびに一時停止します。この一時停止中に、他の INSERT、UPDATE、または DELETE コマンドをデータベース内の他のテーブルに対して実行できます。ただし、元の SELECT ステートメントが終了するまで、これらの変更はコミットされません。

于 2011-12-09T05:57:14.173 に答える