Изящный Первичный Ключ Обработка ошибок в Python / psycopg2 - PullRequest
18 голосов
/ 14 декабря 2011

Использование Python 2.7 и

In [150]: psycopg2. версия Out [150]: '2.4.2 (dt dec pq3 ext)'

Iиметь простые скрипты 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 ]

22 голосов
/ 14 декабря 2011

Вы должны откатить транзакцию при ошибке.

Я добавил еще одну 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]
2 голосов
/ 14 декабря 2011

Прежде всего: 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()})       
...