13

そのため、一度にメモリに保持できない大規模なデータベースがあります。テーブル内のすべてのアイテムをループして処理し、処理されたデータをテーブル内の別の列に配置する必要があります。

カーソルをループしているときに、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つの接続/カーソルオブジェクトがあり、一時データベースに定期的に挿入しながら、選択したレコードセットをループし続けることができます。選択したレコードセットの最後に到達したら、一時データベースのデータを元のデータベースに戻します。

誰かが接続/カーソルオブジェクトについて確かに知っているなら、コメントで知らせてください。

4

3 に答える 3

3

あなたはおおよそ正しいアーキテクチャを持っていると思います-「カーソル」の観点からそれを提示すると、「古いSQLの手」を混乱させるでしょう。なぜなら、彼らは、、、、および他のそのような美しさに関連する多くの問題を考えているからですDECLARE foo CURSORSQLカーソル。Python DB APIの「カーソル」は、SQLステートメントをパッケージ化して実行するための便利な方法であり、必ずしもSQLカーソルに接続されている必要はありません。これらの問題は発生しませんが、(完全にオリジナルの)独自のカーソルが表示される場合があります。 -)しかし、実行している結果の「バッチ処理」、適切なコミットなどにより、私が念頭に置いていた「元の問題」のほとんどを予防​​的に精査しました。FETCH FROM CURSORWHERE CURRENT OF CURSOR

他のいくつかのエンジンでは、最初に一時テーブルを選択してから、プライマリテーブルを更新しながらその一時テーブルから読み取ることをお勧めしますが、使用しているインデックスによっては、sqliteでパフォーマンスがどのように影響を受けるかはわかりません(更新の影響を受けるインデックスがない場合、そのような一時テーブルはsqliteではまったく最適化されない可能性がありますが、パフォーマンス仮説を確認する唯一の実際の方法であるデータに対してベンチマークを実行することはできません)。

だから、私は言うだろう、それのために行く!-)

于 2009-09-23T04:16:26.977 に答える
2

コンテンツを処理するDB関数を作成することは可能ですか?もしそうなら、あなたは単一の更新ステートメントを書いて、データベースにすべての仕事をさせることができるはずです。例えば;

Update data_table
set processed_col = Process_Column(col_to_be_processed)
于 2009-09-24T19:14:56.257 に答える
1

カーソルは、さまざまな理由で悪い悪い悪いです。

CURSORルートを使用する代わりに、単一のUPDATEステートメントを使用することをお勧めします(そして他の多くの人は間違いなくチャイムを鳴らします)。

Processed_Contentをパラメーターとして、次のようなセットベースの操作を行う単一のクエリに送信できますか?

UPDATE data_table
SET processed_content = ?
WHERE processed_content IS NULL
LIMIT 1000

回答に基づいて編集:

すべての行にはProcessed_Contentの一意の値があるため、レコードセットとループを使用する以外に選択肢はありません。私は過去に何度もこれを行いました。あなたが提案していることは効果的に機能するはずです。

于 2009-09-22T20:55:13.857 に答える