2011-12-13 7 views
17

Python 2.7およびPython/psycopg2のグレースフルプライマリキーエラー処理

[150]:psycopg2。 バージョン アウト[150]:「2.4.2(12月のPQ3の内線DT」)

私は、トランザクションを処理する簡単なPythonスクリプトを持っているし、データベースにデータを書き込みます。時々、私の主キーに違反する挿入物があります。これは問題ありません。私はそのレコードを無視して、それをメリーに続けてほしいだけです。私が抱えている問題は、psycopg2の主キーエラーがトランザクションブロック全体を中止し、エラーが失敗した後にすべての挿入が行われてしまうことです。ここには例エラーがあります

ERROR: duplicate key value violates unique constraint "encounter_id_pkey" 
DETAIL: Key (encounter_id)=(9012235) already exists. 

これは次の挿入にあります。違反ではありません。

Inserting: 0163168~9024065 
ERROR: current transaction is aborted, commands ignored until end of transaction block 

2番目のエラーは、挿入するたびに繰り返されます。ここには単純化されたループがあります。私はパンダのデータフレームをループしていますが、それはどんなループでもかまいません。

conn = psycopg2.connect("dbname='XXXX' user='XXXXX' host='XXXX' password='XXXXX'") 

cur = conn.cursor() 

for i, val in df2.iteritems(): 
    try: 
     cur = conn.cursor() 
     cur.execute("""insert into encounter_id_table (
     encounter_id,current_date ) 
     values  
     (%(create_date)s, %(encounter_id)s) ;""", 
     'encounter_id':i.split('~')[1], 
     'create_date': datetime.date.today() })   
     cur.commit() 
     cur.close() 
    except Exception , e: 
     print 'ERROR:', e[0] 
     cur.close() 
conn.close() 

また、基本的な考え方はエラーを正常に処理することです。海軍のネルソン提督の言葉によれば、「操縦はまっすぐに進む」私の場合、エラーはまっすぐに進みます。 "私は、トランザクションブロックをリセットするすべてのインサートのカーソルを開くことで考えました。プライマリキーエラーのために接続をリセットする必要はありません。そこに何か私はちょうど

ジョン

答えて

19

あなたがエラーでトランザクションをロールバックする必要があります。お時間のために?手の前に

感謝をしないのです、私は、コード怒鳴るでもう1件のtry..except..else工事を追加しました例外が発生する正確な場所を表示します。

try: 
    cur = conn.cursor() 

    try: 
     cur.execute("""insert into encounter_id_table (
      encounter_id,current_date ) 
      values  
      (%(create_date)s, %(encounter_id)s) ;""", 
      'encounter_id':i.split('~')[1], 
      'create_date': datetime.date.today() }) 
    except psycopg2.IntegrityError: 
     conn.rollback() 
    else: 
     conn.commit() 

    cur.close() 
except Exception , e: 
    print 'ERROR:', e[0] 
+0

はほとんど答えです。しかし、ここでは接続オブジェクト(conn)のrollback()メソッドを呼び出す必要があります。カーソルはロールバックしないコミットのみを持ちます。ありがとう – jdennison

+0

メソッドエラー。カーソルではなくコネクション上でコミットを呼び出さなければなりません。 – jdennison

+0

私はコミットとロールバックが接続方法であると思っていました。誰かが私のコードを編集して修正するべきだと考えてください。悲しいことに、私はORMや非リレーショナルデータベースを使っているので、直接psycopgの使用に慣れていません。 – lig

2

まず、CURRENT_DATEは、すべてのSQL標準とPostgreSQLの予約語です。二重引用符を付けずに識別子として使用することはできません。私はそれを全く使わないよう強くアドバイスします。

(%(create_date)s, %(encounter_id)s) 

は次のようになります:

私は

次は私がPythonの構文には専門家でありませんが、あなたは、インサート列の順序を逆転しているように見える、私の例ではcurdateに列を名前を変更しましたあなたの主な質問へ

(%(encounter_id)s, %(create_date)s) 

INSERT INTO encounter_id_table (encounter_id, curdate) 
SELECT 1234, now()::date 
WHERE NOT EXISTS (SELECT * FROM encounter_id_table t 
        WHERE t.encounter_id = 1234); 
0:キーが挿入コマンドで使用する前に、テーブルにすでに存在する場合は、チェックすることによって、完全に問題を回避することができますPythonの構文で

は、それは次のようになります。これは

cur.execute("""INSERT INTO encounter_id_table (encounter_id, curdate) 
    SELECT %(encounter_id)s, %(create_date)s, 
    WHERE NOT EXISTS (
      SELECT * FROM encounter_id_table t 
      WHERE t.encounter_id = %(encounter_id)s);""", 
    {'encounter_id':i.split('~')[1], 
    'create_date': datetime.date.today()})  
+0

分離レベルを特に厳密に設定しない限り、WHERE NOT EXISTS構文を使用すると引き続き競合状態が作成されます。 – lusional

+1

@lusional:Postgres 9.5以降では、 'INSERT .. ON CONFLICT DO NOTHING'を使用するのが最も適しています。比較:http://stackoverflow.com/questions/17267417/how-to-upsert-merge-insert-on-duplicate-update-in-postgresql分離レベルを 'Serializable'に変更することは、より高価になります。 –

+0

合意 - 可能であれば、INSERT .. ON CONFLICT構文を使用する方がはるかに優れています。レースの条件は適切に処理されていないと厄介です。 – lusional