какой лучший способ эмулировать "insert ignore" и "on duplicate key update" с postgresql?
Как эмулировать "insert ignore" и "on duplicate key update" (sql merge) с 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$;