Некоторые серверы SQL имеют функцию, INSERT
которая пропускается, если это нарушает ограничение первичного / уникального ключа. Например, MySQL имеет INSERT IGNORE
.
Какой самый лучший способ подражать INSERT IGNORE
и ON DUPLICATE KEY UPDATE
с PostgreSQL?
database
postgresql
rules
гпилотино
источник
источник
ON DUPLICATE KEY UPDATE
в PgSQL 9.5 все еще в некоторой степени невозможно, потому чтоON CLAUSE
эквивалент PgSQL требует, чтобы вы указали имя ограничения, в то время как MySQL может захватывать любое ограничение без необходимости его определения. Это не позволяет мне «эмулировать» эту функцию без переписывания запросов.Ответы:
Попробуйте сделать ОБНОВЛЕНИЕ. Если он не изменяет ни одной строки, что означает, что она не существует, сделайте вставку. Очевидно, вы делаете это внутри транзакции.
Вы, конечно, можете обернуть это функцией, если не хотите размещать дополнительный код на стороне клиента. Вам также понадобится цикл для очень редкого состояния гонки в этом мышлении.
Пример этого есть в документации: http://www.postgresql.org/docs/9.3/static/plpgsql-control-structures.html , пример 40-2 справа внизу.
Обычно это самый простой способ. Вы можете творить чудеса с правилами, но, скорее всего, это будет намного сложнее. Я бы порекомендовал использовать функциональный подход вместо этого в любой день.
Это работает для значений одной или нескольких строк. Если вы имеете дело с большим количеством строк, например, из подзапроса, вам лучше всего разделить его на два запроса, один для INSERT и один для UPDATE (в качестве подходящего соединения / подзапроса, конечно - нет необходимости писать свой основной фильтровать дважды)
источник
INSERT ... ON CONFLICT DO NOTHING;
. См. Также ответ stackoverflow.com/a/34639631/2091700 .MERGE
является не параллелизм безопасным upsert, если не принятьLOCK TABLE
первый. Люди так используют, но это неправильно.В PostgreSQL 9.5 это теперь встроенная функциональность (как в MySQL уже несколько лет):
...
INSERT INTO user_logins (username, logins) VALUES ('Naomi',1),('James',1) ON CONFLICT (username) DO UPDATE SET logins = user_logins.logins + EXCLUDED.logins;
источник
Изменить: если вы пропустили ответ Уоррена , 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
операциям до тех пор, пока оно не будет отброшено, поэтому не совсем однозначно.источник
another_schema.my_table
содержит дубликаты в соответствии с ограничениямиmy_table
?INSERT INTO "my_table" SELECT DISTINCT ON (pk_col_1, pk_col_2) * FROM the_tmp_table;
DELETE FROM my_table WHERE ctid IN (SELECT ctid FROM (SELECT ctid,ROW_NUMBER() OVER (PARTITION BY pk_col_1,pk_col_2) AS rn FROM my_table) AS dups WHERE dups.rn > 1);
Для тех из вас, у кого Postgres 9.5 или выше, должен работать новый синтаксис ON CONFLICT DO NOTHING :
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;
источник
Unique violation: 7 ERROR: duplicate key value violates unique constraint
когда вtarget_table
него была вставлена другая строка во время выполнения этого запроса, если их ключи действительно дублируют друг друга. Я считаю, что блокировкаtarget_table
поможет, но явно пострадает параллелизм.ON CONFLICT (field_one) DO NOTHING
это лучшая часть ответа.Чтобы получить логику игнорирования вставки, вы можете сделать что-то вроде ниже. Я обнаружил, что лучше всего работает простая вставка буквальных значений из оператора 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')
источник
Похоже, PostgreSQL поддерживает объект схемы, называемый правилом .
http://www.postgresql.org/docs/current/static/rules-update.html
Вы можете создать правило
ON INSERT
для данной таблицы, сделав егоNOTHING
действующим, если существует строка с данным значением первичного ключа, или заставить его выполнятьUPDATE
вместо,INSERT
если существует строка с данным значением первичного ключа.Я сам этого не пробовал, поэтому не могу говорить о своем опыте или привести пример.
источник
Как отметил @hanmari в своем комментарии. при вставке в таблицы postgres лучше всего использовать код при конфликте (..) ничего не делать, чтобы не вставлять повторяющиеся данные .:
query = "INSERT INTO db_table_name(column_name) VALUES(%s) ON CONFLICT (column_name) DO NOTHING;"
Строка кода ON CONFLICT позволит оператору вставки по-прежнему вставлять строки данных. Код запроса и значений - это пример вставленной даты из Excel в таблицу postgres db. У меня есть ограничения, добавленные в таблицу postgres, которую я использую, чтобы убедиться, что поле идентификатора уникально. Вместо того, чтобы запускать удаление одинаковых строк данных, я добавляю строку кода sql, которая перенумеровывает столбец идентификатора, начиная с 1. Пример:
q = 'ALTER id_column serial RESTART WITH 1'
Если в моих данных есть поле идентификатора, я не использую его в качестве основного идентификатора / серийного идентификатора, я создаю столбец идентификатора и устанавливаю его на серийный номер. Надеюсь, эта информация будет полезна всем. * У меня нет высшего образования в области разработки / программирования программного обеспечения. Все, что знаю в кодировании, изучаю самостоятельно.
источник
Это решение позволяет избежать использования правил:
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 ):
источник
При массовом использовании вы всегда можете удалить строку перед вставкой. Удаление несуществующей строки не вызывает ошибки, поэтому ее можно безопасно пропустить.
источник
DEFERRABLE INITIALLY DEFERRED
флагами.Для скриптов импорта данных, чтобы заменить «ЕСЛИ НЕ СУЩЕСТВУЕТ», в некотором смысле есть несколько неудобная формулировка, которая, тем не менее, работает:
DO $do$ BEGIN PERFORM id FROM whatever_table; IF NOT FOUND THEN -- INSERT stuff END IF; END $do$;
источник