Python / psycopg2中的优雅主键错误处理

时间:2011-12-13 23:48:01

标签: python sql postgresql psycopg2

使用Python 2.7和

在[150]中:psycopg2。版本 Out [150]:'2.4.2(dt dec pq3 ext)'

我有一个简单的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

每次插入都会重复第二个错误。这是一个简化的循环。我正在循环一个pandas数据框,但它可能是任何循环。

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()   

同样基本的想法是优雅地处理错误。在皇家海军纳尔逊海军上将的格言:“该死的演习直接对他们说”。或者在我们的情况下,该错误直接对他们说。“我想通过在每个插件上打开一个光标我将重置事务块。我不想因为主键错误而重置连接。是有什么我不知道的吗?

先谢谢你的时间。

约翰

2 个答案:

答案 0 :(得分:22)

您应该在出错时回滚交易。

我在下面的代码中添加了一个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]

答案 1 :(得分:2)

首先:CURRENT_DATE是每个SQL标准以及PostgreSQL中的保留字。如果不对其进行双引号,则不能将其用作标识符。我强烈建议不要使用它。我在我的示例中将列重命名为curdate

接下来,我不是python语法方面的专家,但你似乎颠倒了插入列的顺序:

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

应该是:

( %(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);

在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()})