Postgresql-аналоги myslq-евской «ON DUPLICATE KEY UPDATE»
В mysql есть очень удобная конструкция
1 |
INSERT INTO table (columns) VALUES (values) ON DUPLICATE KEY UPDATE column1=value1, column2=value2 |
Это очень удобно тогда, когда нужно вставить только те данные, которых в таблице нет (при этом в апдейте указывается уже существующее значение поля).
В postgresql такого, к сожалению, нет.
Но подобное поведение можно сэмулировать несколькими способами. Два самых интересных:
1. Создать правило
1 2 3 4 5 6 |
CREATE OR REPLACE RULE "insert_ignore_mytable" AS ON INSERT TO "mytable" WHERE EXISTS ( SELECT 1 FROM "mytable" WHERE id = NEW.id ) DO INSTEAD NOTHING; |
Этот способ удобен тем, что его нужно написать только один раз, и при любой вставке данных с уже существующим ключом выполнение работы продолжится, а самой вставки не произойдёт. Неудобен тем, что выполняется селект, да и вообще это по сути триггер, делающий лишнюю работу до вставки.
2. Обработать исключение
1 2 3 4 5 |
BEGIN INSERT INTO db(a,b) VALUES (key, data); EXCEPTION WHEN unique_violation THEN -- смотрим, улыбаемся END; |
Удобен тем, что не надо затрагивать таблицу, т.е. другие запросы пойдут как обычно. Неудобен тем, что его приходится использовать внутри хранимых процедур.
3. Вполне полноценная замена (начиная с 9.5 — https://www.postgresql.org/docs/9.5/sql-insert.html):
1 2 3 4 5 6 |
INSERT INTO table (id, value1, value2) VALUES (1, 2, 1) ON CONFLICT ( id, value1) DO UPDATE SET value2 = value2 + 1; |
Similar Posts
LEAVE A COMMENT
Для отправки комментария вам необходимо авторизоваться.
Для частных случаев можно оптимизировать в некотором роде. Например, если UPDATE-ов предполагается гораздо больше чем INSERT-ов, то сначала пытаемся сделать UPDATE, проверяем кол-во обработанных записей, и если таких нет, делаем INSERT.
А по поводу селекта в первом варианте — сильно волноваться, имхо, не стоит, поскольку необходимые для доступа к этой записи дисковые страницы в любом случае будут прочитаны одинаковое количество раз, а запись обычно полезно лочить в транзакции, что вполне можно сюда прикрутить.
1. Работает как надо, но для создания 3000000 записей из множества таблиц потребовалось 1 час, это я еще и вызывал функцию из самого postgres которая выполняет INSERT. НО все отработало корректно.
2. Полностью согласен с комментарием «alex», часть записей при возникновении ошибки «уходит» в никуда, за то отработало за 6 минут но 5% записей не сгенерировалось.