2014-12-22 2 views
1

Я пытаюсь вставить строку, если один и тот же первичный ключ еще не существует (в этом случае игнорировать). Выполнение этого с Python, используя psycopg2 и Postgres версии 9.3.Штрафы за INSERT с существующим первичным ключом?

Существует несколько вариантов, как это сделать: 1) использовать подзапрос, 2) использовать транзакцию, 3) разрешить ей сбой.

кажется проще сделать что-то вроде этого:

try: 
    cursor.execute('INSERT...') 
except psycopg2.IntegrityError: 
    pass 

Существуют ли какие-либо недостатки этого подхода? Есть ли какой-либо штраф за выполнение с провалом?

+1

Хороший вопрос. По моему опыту, подзапрос с «НЕ СУЩЕСТВУЕТ» самый дешевый (хотя только 99,99% безопасен в отношении условий гонки), но я не могу указывать числа на стоимость повышения исключения. Это также во многом зависит от скорости ключевых столкновений. –

ответ

1

Надежный способ сделать это в данный момент - попробовать вставить и дать ему потерпеть неудачу. Вы можете сделать это на уровне приложения или на уровне Postgres; предполагая, что он не является частью процедуры, выполняемой на сервере, это не имеет большого значения, если это так или иначе, когда дело доходит до производительности, поскольку вы отправляете запрос на сервер и получаете результат. (Если это имеет значение, вам необходимо определить точку сохранения, если вы пытаетесь выполнить транзакцию по той же причине. Или, как указано в ответе Крейга, если у вас много неудачных заявлений.)

В будущих версиях, надлежащее merge и upsert находятся на радаре, но как около десятилетия долгой дискуссии предположит их реализации должным образом довольно тернист:

Что касается других опций, о которых вы упомянули, указанные выше страницы вики и ссылки внутри них должны подчеркнуть трудности. В принципе, использование подзаголовка является дешевым, как отметил Эрвин, но не является доказательством параллелизма (если вы не заблокируете его правильно); использование блокировок в основном сводится к блокировке всей таблицы (тривиальная, но не большая) или изобретать колесо, которое подделывается в ядре (тривиально для существующих строк, а тем более для потенциально новых, которые вставлены одновременно, если они пытаются использовать предикаты вместо таблицы, блокировка уровня); и использование транзакции и перехват исключения - это то, что вы в конечном итоге сделаете.

+0

'MERGE' абсолютно * не * решение для upsert. Обычно это и неправильно злоупотребляют для других СУБД, но на самом деле это небезопасно, так как «MERGE» не предлагает атомную поддержку. Это не лучше, чем «не существует». –

+0

Кроме того, * невозможно * использовать блокировку уровня строки для правильной реализации upsert, потому что вам нужно будет иметь возможность блокировки предикатов для ключа, которая не поддерживается. –

+0

@CraigRinger: возможно использование блокировки на уровне таблицы или (эквивалентно) блокировки предикатов, например. имя таблицы. –

0

Работа продолжается, чтобы добавить родной upsert к PostgreSQL 9.5, which will probably take the form of an INSERT ... ON CONFLICT UPDATE ... statement.

В то же время, вы должны попытаться обновить, и если это не удастся, повторите попытку. Нет никакой безопасной альтернативы, хотя вы можете loop within a PL/PgSQL function, чтобы скрыть это от приложения.

Re пытается и позволить ему неудачу:

Есть ли какие-либо недостатки этого подхода?

Это создает большой объем раздражающего шума в файлах журнала. Он также сжигает идентификаторы транзакций очень быстро, если скорость конфликта высока, потенциально требуя более частых VACUUM FREEZE, которые будут запускаться autovacuum, что может быть проблемой для больших баз данных.

Есть ли штраф за проделанную работу с отказом?

Если ставка конфликта высока, вы будете делать кучу дополнительных круглых поездок в базу данных. Иначе не очень-то реально.

Смежные вопросы