Как эмулировать "insert ignore" и "on duplicate key update" (sql merge) с postgresql?

какой лучший способ эмулировать "insert ignore" и "on duplicate key update" с postgresql?

Ответ 1

Попробуйте сделать ОБНОВЛЕНИЕ. Если он не изменяет ни одну строку, что означает, что она не существует, то и вставка. Очевидно, что вы делаете это внутри транзакции.

Вы можете, конечно, обернуть это в функцию, если не хотите добавлять дополнительный код на стороне клиента. Вам также нужна петля для очень редкого состояния гонки в этом мышлении.

Вот пример этого в документации: http://www.postgresql.org/docs/9.3/static/plpgsql-control-structures.html, пример 40-2 внизу внизу.

Это обычно самый простой способ. Вы можете сделать магию с правилами, но это, вероятно, будет намного более грязным. В любой день я бы рекомендовал подход "в обход".

Это работает для значений одной строки или нескольких строк. Если вы имеете дело с большим количеством строк, например, из подзапроса, вам лучше всего разбить его на два запроса: один для INSERT и один для UPDATE (как подходящее соединение/подзапрос, конечно - нет необходимости писать свой основной фильтр дважды)

Ответ 2

Изменить: если вы пропустили warren answer, PG9.5 теперь имеет это изначально; время для обновления!


Основываясь на Билле Карвине, объясните, как будет выглядеть подход, основанный на правилах (передача из другой схемы в том же БД и с помощью многоколоночного первичного ключа):

CREATE RULE "my_table_on_duplicate_ignore" AS ON INSERT TO "my_table"
  WHERE EXISTS(SELECT 1 FROM my_table 
                WHERE (pk_col_1, pk_col_2)=(NEW.pk_col_1, NEW.pk_col_2))
  DO INSTEAD NOTHING;
INSERT INTO my_table SELECT * FROM another_schema.my_table WHERE some_cond;
DROP RULE "my_table_on_duplicate_ignore" ON "my_table";

Примечание. Это правило применяется ко всем операциям INSERT до тех пор, пока правило не будет отброшено, поэтому не совсем ad hoc.

Ответ 3

С PostgreSQL 9.5 теперь это встроенная функция (например, MySQL имеет в течение нескольких лет):

INSERT... ON CONFLICT НИЧЕГО/ОБНОВЛЕНИЕ ( "UPSERT" )

9.5 обеспечивает поддержку операций "UPSERT". INSERT расширен, чтобы принять предложение ON CONFLICT DO UPDATE/IGNORE. В этом предложении указывается альтернативное действие, которое необходимо предпринять в случае возможного дублирования нарушения.

...

Дополнительный пример нового синтаксиса:

INSERT INTO user_logins (username, logins)
VALUES ('Naomi',1),('James',1) 
ON CONFLICT (username)
DO UPDATE SET logins = user_logins.logins + EXCLUDED.logins;

Ответ 4

Чтобы получить логику вставить игнорировать, вы можете сделать что-то вроде ниже. Я просто попробовал вставить из выражения выбора литеральных значений, которые лучше всего работают, тогда вы можете замаскировать дубликаты ключей с помощью предложения NOT EXISTS. Чтобы получить обновление по дублирующей логике, я подозреваю, что цикл pl/pgsql будет необходим.

INSERT INTO manager.vin_manufacturer
(SELECT * FROM( VALUES
  ('935',' Citroën Brazil','Citroën'),
  ('ABC', 'Toyota', 'Toyota'),
  ('ZOM',' OM','OM')
  ) as tmp (vin_manufacturer_id, manufacturer_desc, make_desc)
  WHERE NOT EXISTS (
    --ignore anything that has already been inserted
    SELECT 1 FROM manager.vin_manufacturer m where m.vin_manufacturer_id = tmp.vin_manufacturer_id)
)

Ответ 5

INSERT INTO mytable(col1,col2) 
    SELECT 'val1','val2' 
    WHERE NOT EXISTS (SELECT 1 FROM mytable WHERE col1='val1')

Ответ 6

Похоже, PostgreSQL поддерживает объект схемы, называемый правилом.

http://www.postgresql.org/docs/current/static/rules-update.html

Вы можете создать правило ON INSERT для данной таблицы, сделав его NOTHING, если строка существует с заданным значением первичного ключа, или же делает ее UPDATE вместо INSERT, если строка существует с заданным значением первичного ключа.

Я не пробовал это сам, поэтому я не могу говорить по опыту или предлагать пример.

Ответ 7

Для тех из вас, у кого есть Postgres 9.5 или выше, новый синтаксис ON CONFLICT DO NOTING должен работать:

INSERT INTO target_table (field_one, field_two, field_three ) 
SELECT field_one, field_two, field_three
FROM source_table
ON CONFLICT (field_one) DO NOTHING;

Для тех из нас, кто имеет более раннюю версию, это право будет работать вместо:

INSERT INTO target_table (field_one, field_two, field_three )
SELECT source_table.field_one, source_table.field_two, source_table.field_three
FROM source_table 
LEFT JOIN target_table ON source_table.field_one = target_table.field_one
WHERE target_table.field_one IS NULL;

Ответ 8

Это решение позволяет избежать использования правил:

BEGIN
   INSERT INTO tableA (unique_column,c2,c3) VALUES (1,2,3);
EXCEPTION 
   WHEN unique_violation THEN
     UPDATE tableA SET c2 = 2, c3 = 3 WHERE unique_column = 1;
END;

но он имеет недостаток производительности (см. PostgreSQL.org):

Блок, содержащий предложение EXCEPTION, значительно дороже для входа и выхода из блока без него. Поэтому не используйте ИСКЛЮЧЕНИЕ без необходимости.

Ответ 9

В основном случае вы всегда можете удалить строку перед вставкой. Удаление строки, которая не существует, не вызывает ошибки, поэтому ее безопасно пропустили.

Ответ 10

Как сказал в своем комментарии @hanmari. при вставке в таблицы postgres конфликт on (..) ничего не делает, это лучший код для использования не для вставки повторяющихся данных.:

query = "INSERT INTO db_table_name(column_name)
         VALUES(%s) ON CONFLICT (column_name) DO NOTHING;"

Строка ON CONFLICT кода позволяет инструкции insert вставлять строки данных. Код запроса и значений является примером вставленной даты из Excel в таблицу db postgres. У меня есть ограничения, добавленные в таблицу postgres, которую я использую, чтобы убедиться, что поле ID уникально. Вместо того, чтобы запускать удаление по строкам данных, это то же самое, я добавляю строку кода sql, которая renumbers столбца идентификатора, начинающегося с 1. Пример:

q = 'ALTER id_column serial RESTART WITH 1'

Если у моих данных есть поле идентификатора, я не использую его как первичный ID/серийный идентификатор, я создаю столбец идентификатора, и я устанавливаю его в последовательный. Надеюсь, эта информация будет полезной для всех. * У меня нет степени в области разработки/кодирования программного обеспечения. Все, что я знаю в кодировании, я изучаю самостоятельно.

Ответ 11

Для сценариев импорта данных, чтобы заменить "ЕСЛИ НЕ СУЩЕСТВУЕТ", в некотором смысле, есть немного неудобная формулировка, которая тем не менее работает:

DO
$do$
BEGIN
PERFORM id
FROM whatever_table;

IF NOT FOUND THEN
-- INSERT stuff
END IF;
END
$do$;