2011-10-18 5 views
1

PythonとPostgresが単一のトランザクションで複数のデータセットを挿入するときのトランザクションと一括挿入の処理方法を理解する必要があります。 環境:名前付き変数と一括挿入を使用するpostgresを使用したPython

  • のWindows 7 64ビット
  • のPython 3.2
  • Postgresqlの9.1
  • psycopg2
ここ

私のシナリオは次のとおりです。私はXMLに1つのデータベース(オラクル)からのデータを変換しています そのデータを新しいデータベース(ポストグル)に挿入します。これは大きなデータセットなので、私はいくつかのインサートを最適化しようとしています。このデータの多くはライブラリタイプのオブジェクトを考慮しているので、ライブラリテーブルがあり、次にxmlメタデータとXMLコンテンツのテーブルがあり、このデータのフィールドはデータベースのテキストタイプです。私はoracleからデータを取り出し、挿入する必要のあるデータの辞書を作成しています。私は3つの挿入ステートメントを持っています。最初の挿入は、シリアルIDを使用してライブラリテーブルにレコードを作成し、そのIDはメタデータとコンテンツテーブルにxmlを挿入する次の2つのクエリの関係に必要です。私は最初のクエリを実行したときに私が持っている問題がある

for inputKey in libDataDict.keys(): 
    metaString = libDataDict[inputKey][0] 
    contentString = libDataDict[inputKey][1] 
    insertLibDataList.append({'objIdent':"%s" % inputKey, 'objName':"%s" % inputKey, objType':libType}) 
    insertMetadataDataList.append({'objIdent':inputKey,'objMetadata':metaString}) 
    insertContentDataList.append({'objIdent':inputKey, 'objContent':contentString}) 

dataDict['cmsLibInsert'] = insertLibDataList 
dataDict['cmsLibMetadataInsert'] = insertMetadataDataList 
dataDict['cmsLibContentInsert'] = insertContentDataList 

sqlDict[0] = {'sqlString':"insert into cms_libraries (cms_library_ident, cms_library_name, cms_library_type_id, cms_library_status_id) \ 
       values (%(objIdent)s, %(objName)s, (select id from cms_library_types where cms_library_type_name = %(objType)s), \ 
       (select id from cms_library_status where cms_library_status_name = 'active'))", 'data':dataDict['cmsLibInsert']} 

sqlDict[1] = {'sqlString':"insert into cms_library_metadata (cms_library_id, cms_library_metadata_data) values \ 
       ((select id from cms_libraries where cms_library_ident = %(objIdent)s), $$%(objMetadata)s$$)", \ 
       'data':dataDict['cmsLibMetadataInsert']} 

sqlDict[2] = {'sqlString':"insert into cms_library_content (cms_library_id, cms_library_content_data) values \ 
       ((select id from cms_libraries where cms_library_ident = %(objIdent)s), $$%(objContent)s$$)", \ 
       'data':dataDict['cmsLibContentInsert']} 

bulkLoadData(myConfig['pgConn'], myConfig['pgCursor'], sqlDict) 

(sqlDict [0])とすべてのものを挿入しない限り、私がそうであるように、それは分離正常に動作します:ここで私が話しているかの例です。私は次の2つを実行する前にコミットします。理想的には、これらのクエリはすべて同じトランザクションで実行したいと思いますが、2番目と3番目のクエリのcms_librariesテーブルからIDを見つけることができないため失敗します。ここ は私の現在の挿入コードです:

def bulkLoadData(dbConn, dbCursor, sqlDict): 
try: 
    libInsertSql = sqlDict.pop(0) 
    dbSql = libInsertSql['sqlString'] 
    data = libInsertSql['data'] 
    dbCursor.executemany(dbSql, data) 
    dbConn.commit() 
    for sqlKey in sqlDict: 
    dbSql = sqlDict[sqlKey]['sqlString'] 
    data = sqlDict[sqlKey]['data'] 
    dbCursor.executemany(dbSql, data) 

    dbConn.commit() 

は、以前私は、クエリに値を追加して、各挿入のためにクエリを実行していました。私がそれをするとき、私はそれをすべて同じトランザクションに入れることができ、生成されたIDとすべてがうまくいきます。私はexecutemany()で一括挿入を行うとidが見つからないのはなぜ分かりませんか?一括挿入と他の2つのクエリを同じトランザクションで実行する方法はありますか?

私はこの文書を読み、stackoverflowのとインターネットを検索するが、私の問題に答えを見つけることができますされています

Postgresql string docs

すべてのヘルプ、提案、またはコメントいただければ幸いです:さんは pyscopg docs などのpostgres 。 ありがとう、 ミッチ

+0

明らかに私は私の質問で回答が得られていないので、質問の2番目の部分を削除して別にそれを聞いてきました。私は誰かがこのことを理解するのを助けることを願っています... – mcfar

+0

SET [log_statement](http://www.postgresql.org/docs/current/interactive/runtime-config-logging.html)= all'を有効にできますか? PostgreSQLサーバのログを確認してください。実際にどのコマンドを受け取ったのですか? –

+0

私は旅行を外出していたので、私は応答が遅いですが、コメントをいただきありがとうございます、それは良い提案です。私はそれを試し、何が起こっているかを明らかにするかどうかを見ます。 – mcfar

答えて

0

ここでは2つの選択肢があります。 IDを外部から生成するか(一括挿入を可能にする)、シリアルからIDを生成するか(これは単一入力挿入を行うことを意味します)。私はそれがかなり簡単に外部IDの生成とバルクロードを考え出す(私はあなたがETL toolではなく、何かを手書きのPythonでコーディングすることをお勧めしたいと思います)。シリアルからIDを取得する必要がある場合は、サーバー側のprepared statementsを考慮する必要があります。

あなたの最初の文は次のようになります。

dbCursor.execute(""" 
PREPARE cms_lib_insert (bigint, text, text) AS 
INSERT INTO cms_libraries (cms_library_ident, cms_library_name, cms_library_type_id, cms_library_status_id) 
VALUES ($1, $2, 
    (select id from cms_library_types where cms_library_type_name = $3), 
    (select id from cms_library_status where cms_library_status_name = 'active') 
) 
RETURNING cms_library.id 
""") 

あなたは起動時に、一度これを実行します。次に、エントリレベルで次のEXECUTE文を実行したいと思うでしょう。

dbCursor.execute(""" 
EXECUTE cms_lib_insert(%(objIndent)s, %(objName)s, %(objType)s) 
""", {'objIndent': 345, 'objName': 'foo', 'objType': 'bar')) 
my_new_id = dbCursor.fetchone()[0] 

これは、生成されたシリアルIDを返します。今後は、データベース通信(sqlDictのアプローチ)を抽象化し、非常に直接的なコーディングパターンを使用しようとしている現在のパターンから離れることを強くお勧めします(賢いところはあなたの敵です。チューニングが難しい)。

パフォーマンスのために機能するブロックサイズに挿入をバッチしたいと思うでしょう。つまり、実際の動作に基づいてBLOCK_SIZEを調整します。あなたのコードは次のようになります。あなたはこれよりも高いパフォーマンスレベルを達成するために必要がある場合は

BLOCK_SIZE = 500 
while not_done: 
    dbCursor.begin() 
    for junk in irange(BLOCK_SIZE): 
     dbCursor.execute("EXECUTE cms_lib_insert(...)") 
     cms_lib_id = dbCursor.fetchone()[0]  # you're using this below. 
     dbCursor.executemany("EXECUTE metadata_insert(...)") 
     dbCursor.executemany("EXECUTE library_insert(...)") 
    dbCursor.commit() 

を、次のステップは、従属テーブルの行の配列をとり、挿入ハンドラ関数を構築しています。私はすぐにメンテナンスの悪夢になるので、これを行うことをお勧めしません。

+0

詳細な回答をいただきありがとうございます。これは大きな助けとなり、私はこれらの変更を運用コードに実装する予定です。 – mcfar

関連する問題