postgresql Python/psycopg 2中的优雅主键错误处理

prdp8dxp  于 2023-06-22  发布在  PostgreSQL
关注(0)|答案(2)|浏览(172)

使用Python 2.7和
In [150]:psycopg2.versionOut[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 Dataframe ,但它可以是任何循环。

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

同样,基本的想法是优雅地处理错误。皇家海军上将纳尔逊(Admiral Nelson)曾说过:“该死的演习直接向他们冲去”。或者在我们的情况下,该死的错误直接指向他们。”我想通过在每个插入上打开一个光标,我将重置事务块。我不想仅仅因为一个主键错误而必须重置连接。是不是有什么我刚刚错过了?
感谢您的时间。
约翰

anhgbhbe

anhgbhbe1#

您应该在出现错误时回滚事务。
我在下面的代码中添加了一个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]
polkgigr

polkgigr2#

首先:CURRENT_DATE是每个SQL标准以及PostgreSQL中的保留字。如果没有双引号,则不能将其用作标识符。我宁愿避免这种情况,因此在示例中使用列名curdate
接下来,您似乎颠倒了插入列的顺序:

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

应为:

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

回答你的主要问题:完全避免这个问题!
因为Postgres 9.5 INSERT ... ON CONFLICT DO NOTHING是要走的路。这也避免了并发写入负载下的竞争条件:

INSERT INTO encounter_id_table (encounter_id, curdate)
VALUES (1234, CURRENT_DATE)
ON CONFLICT DO NOTHING;

参见:

在Python语法中,应该是:

cur.execute("""INSERT INTO encounter_id_table (encounter_id, curdate)
    VALUES (%(encounter_id)s, CURRENT_DATE);""",
  {'encounter_id':i.split('~')[1]})

在旧版本中,你可以通过在插入键之前检查它是否已经在表中来避免这个问题:

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

但在并发写负载下可能会失败。

相关问题