Почему это правило не предотвращает дублирование ключевых нарушений?

(postgresql) Я пытался вставлять COPY csv данные в таблицу, но у меня были ошибки дублирования ключевого слова, и нет способа сказать COPY игнорировать их, поэтому, следуя мудрости в Интернете, я попытался добавить это правило:

CREATE OR REPLACE RULE ignore_duplicate_inserts AS
   ON INSERT TO mytable
   WHERE (EXISTS ( SELECT mytable.id
           FROM mytable
          WHERE mytable.id = new.id)) DO NOTHING;

чтобы обойти проблему, но я все еще получаю эти ошибки - любые идеи, почему?

Ответ 1

По умолчанию по умолчанию добавляет вещи в текущее действие:

Грубо говоря, правило вызывает дополнительные команды для выполнения, когда выполняется заданная команда в данной таблице.

Но правило INSTEAD позволяет вам заменить действие:

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

Итак, я думаю, вы хотите указать INSTEAD:

CREATE OR REPLACE RULE ignore_duplicate_inserts AS
   ON INSERT TO mytable
   WHERE (EXISTS ( SELECT mytable.id
           FROM mytable
          WHERE mytable.id = new.id)) DO INSTEAD NOTHING;

Без INSTEAD ваше правило по существу говорит "сделайте INSERT, а затем ничего не делать", когда вы хотите сказать "вместо INSERT, ничего не делать", и AFAIK, DO INSTEAD NOTHING сделает это.

Я не эксперт по правилам PostgreSQL, но я думаю, что добавление "INSTEAD" должно работать.

ОБНОВЛЕНИЕ: спасибо araqnid мы знаем, что:

COPY FROM вызовет любые триггеры и проверит ограничения в таблице адресатов. Однако он не будет вызывать правила

Таким образом, в этой ситуации правило не будет работать. Тем не менее, триггеры запускаются во время COPY FROM, поэтому вы можете написать триггер ДОБАВИТЬ

Ответ 2

COPY FROM не будет вызывать правила (http://www.postgresql.org/docs/9.0/interactive/sql-copy.html#AEN58860)

Моим подходом было бы загрузить CSV-данные в временную таблицу, а затем использовать оператор INSERT...SELECT для копирования данных в целевую таблицу, где она еще не существует. (Если в данных CSV есть дубликаты, сначала удалите их из таблицы temp). Что-то вроде:

BEGIN;
CREATE TEMP TABLE stage_data(key_column, data_columns...) ON COMMIT DROP;
\copy stage_data from data.csv with csv header
-- prevent any other updates while we are merging input (omit this if you don't need it)
LOCK target_data IN SHARE ROW EXCLUSIVE MODE;
-- insert into target table
INSERT INTO target_data(key_column, data_columns...)
   SELECT key_column, data_columns...
   FROM stage_data
   WHERE NOT EXISTS (SELECT 1 FROM target_data
                     WHERE target_data.key_column = stage_data.key_column)
END;