как эмулировать" insert ignore "и" on duplicate key update " (слияние sql) с postgresql?
некоторые SQL-серверы имеют функцию, где INSERT
пропускается, если это нарушит ограничение первичного / уникального ключа. Например, MySQL имеет INSERT IGNORE
.
какой лучший способ подражать INSERT IGNORE
и ON DUPLICATE KEY UPDATE
С PostgreSQL?
11 ответов
попробуйте сделать обновление. Если он не изменяет какую-либо строку, это означает, что она не существовала, поэтому сделайте вставку. Очевидно, вы делаете это внутри транзакции.
вы можете, конечно, обернуть это в функцию, если вы не хотите помещать дополнительный код на стороне клиента. Вам также нужен цикл для очень редкого состояния расы в этом мышлении.
в документации есть пример этого:http://www.postgresql.org/docs/9.3/static/plpgsql-control-structures.html, пример 40-2 справа внизу.
это обычно самый простой способ. Вы можете делать магию с правилами, но это, вероятно, будет намного грязнее. Я бы рекомендовал подход wrap-in-function в любой день.
Это работает для одной строки или нескольких строк, значения. Если вы имеете дело с большим количеством строк, например, из подзапроса, вам лучше разделить его на два запроса: один для вставки и один для обновления (как соответствующее соединение / подзапрос, конечно , - нет необходимости напишите свой основной фильтр дважды)
С PostgreSQL 9.5, это теперь стандартные функции (как MySQL имел в течение нескольких лет):
вставить ... ПРИ КОНФЛИКТЕ НИЧЕГО НЕ ДЕЛАТЬ / ОБНОВИТЬ ("UPSERT")
9.5 обеспечивает поддержку операций "UPSERT". Вставка расширена, чтобы принять предложение 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;
Edit: в случае, если вы пропустили Уоррен, 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.
для получения вставить игнорировать логики вы можете сделать что-то вроде ниже. Я нашел, что просто вставка из инструкции select литеральных значений работала лучше всего, тогда вы можете замаскировать дубликаты ключей предложением 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)
)
INSERT INTO mytable(col1,col2)
SELECT 'val1','val2'
WHERE NOT EXISTS (SELECT 1 FROM mytable WHERE col1='val1')
для тех из вас, кто имеет Postgres 9.5 или выше, новый НА КОНФЛИКТ НИЧЕГО НЕ ДЕЛАТЬ синтаксис должен работать:
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;
похоже, PostgreSQL поддерживает объект схемы, называемый правила.
http://www.postgresql.org/docs/current/static/rules-update.html
вы можете создать правило ON INSERT
для данной таблицы, что делает егоNOTHING
если строка существует с заданным значением первичного ключа или делает ее UPDATE
вместо INSERT
если строка существует с заданным значением первичного ключа.
Я не пробовал это сам, поэтому я не могу говорить из опыта или предложите пример.
это решение позволяет избежать использования правил:
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):
блок, содержащий предложение исключения, значительно дороже чтобы войти и выйти, чем блок без одного. Поэтому не используйте Исключение без необходимости.
как @hanmari упомянул в своем комментарии. при вставке в таблицы postgres, on conflict (..) do nothing-лучший код для использования, чтобы не вставлять дубликаты данных.:
query = "INSERT INTO db_table_name(column_name)
VALUES(%s) ON CONFLICT (column_name) DO NOTHING;"
строка кода on CONFLICT позволит инструкции insert по-прежнему вставлять строки данных. Код запроса и значений является примером вставленной даты из Excel в таблицу БД postgres. У меня есть ограничения, добавленные в таблицу postgres, которую я использую, чтобы убедиться, что поле ID уникально. Вместо запуска удаление в строках данных, которые одинаковы, я добавляю строку кода sql, которая переименовывает столбец ID, начиная с 1. Пример:
q = 'ALTER id_column serial RESTART WITH 1'
Если мои данные имеют поле ID, я не использую это в качестве основного ID / serial ID, я создаю столбец ID и устанавливаю его в serial. Надеюсь, эта информация будет полезна всем. *У меня нет диплома колледжа по разработке/кодированию программного обеспечения. Все, что я знаю в кодировании, я изучаю сам.
в bulk вы всегда можете удалить строку перед вставкой. Удаление строки, которая не существует, не вызывает ошибки, поэтому ее безопасно пропустили.
для скриптов импорта данных, чтобы заменить "если не существует", в некотором роде, есть немного неудобная формулировка, которая тем не менее работает:
DO
$do$
BEGIN
PERFORM id
FROM whatever_table;
IF NOT FOUND THEN
-- INSERT stuff
END IF;
END
$do$;