そのため、一度にメモリに保持できない大規模なデータベースがあります。テーブル内のすべてのアイテムをループして処理し、処理されたデータをテーブル内の別の列に配置する必要があります。
カーソルをループしているときに、updateステートメントを実行しようとすると、レコードセットが切り捨てられます(カーソルオブジェクトを再利用しているためだと思います)。
質問:
updateステートメントを実行するための2番目のカーソルオブジェクトを作成すると、元のselectステートメントをループし続けることができますか?
2番目のカーソルオブジェクトを作成するには、データベースへの2番目の接続が必要ですか?これにより、これを実行できますか?
sqliteは、データベースへの2つの接続(1つはテーブルからの読み取り、もう1つはデータベースへの書き込み)にどのように応答しますか?
私のコード(簡略化):
import sqlite3
class DataManager():
""" Manages database (used below).
I cut this class way down to avoid confusion in the question.
"""
def __init__(self, db_path):
self.connection = sqlite3.connect(db_path)
self.connection.text_factory = str
self.cursor = self.connection.cursor()
def genRecordset(self, str_sql, subs=tuple()):
""" Generate records as tuples, for str_sql.
"""
self.cursor.execute(str_sql, subs)
for row in self.cursor:
yield row
select = """
SELECT id, unprocessed_content
FROM data_table
WHERE processed_content IS NULL
"""
update = """
UPDATE data_table
SET processed_content = ?
WHERE id = ?
"""
data_manager = DataManager(r'C:\myDatabase.db')
subs = []
for row in data_manager.genRecordset(str_sql):
id, unprocessed_content = row
processed_content = processContent(unprocessed_content)
subs.append((processed_content, id))
#every n records update the database (whenever I run out of memory)
if len(subs) >= 1000:
data_manager.cursor.executemany(update, subs)
data_manager.connection.commit()
subs = []
#update remaining records
if subs:
data_manager.cursor.executemany(update, subs)
data_manager.connection.commit()
私が試したもう1つの方法は、selectステートメントを次のように変更することでした。
select = """
SELECT id, unprocessed_content
FROM data_table
WHERE processed_content IS NULL
LIMIT 1000
"""
それから私はします:
recordset = data_manager.cursor.execute(select)
while recordset:
#do update stuff...
recordset = data_manager.cursor.execute(select)
これに関して私が抱えていた問題は、実際のselectステートメントにJOINが含まれていて時間がかかるため、JOINを何度も実行すると非常に時間がかかることでした。選択を1回だけ実行し、次にジェネレーターを使用してプロセスを高速化しようとしているので、すべてをメモリに保持する必要はありません。
解決:
さて、私の最初の2つの質問に対する答えは「いいえ」です。3番目の質問ですが、データベースへの接続が確立されると、データベース全体がロックされるため、最初の接続が閉じられるまで、別の接続は何もできなくなります。
ソースコードは見つかりませんでしたが、経験的な証拠から、接続では一度に1つのカーソルオブジェクトしか使用できず、最後に実行されたクエリが優先されると思います。これは、選択したレコードセットをループして一度に1行ずつ生成しているときに、最初の更新ステートメントを実行するとすぐに、ジェネレーターが行の生成を停止することを意味します。
私の解決策は、processed_contentをidで固定する一時データベースを作成することです。これにより、データベースごとに1つの接続/カーソルオブジェクトがあり、一時データベースに定期的に挿入しながら、選択したレコードセットをループし続けることができます。選択したレコードセットの最後に到達したら、一時データベースのデータを元のデータベースに戻します。
誰かが接続/カーソルオブジェクトについて確かに知っているなら、コメントで知らせてください。