#sql #postgresql #sql-insert #multiple-insert
#sql #postgresql #sql-вставка #множественная вставка
Вопрос:
Допустим, у меня есть schools
таблица
CREATE TABLE "public"."schools" (
"id" text NOT NULL,
"ancestor_id" text,
CONSTRAINT "schools_ancestor_id_fkey" FOREIGN KEY ("ancestor_id") REFERENCES "public"."schools"("id"),
PRIMARY KEY ("id")
);
Я хочу вставить несколько значений для schools
INSERT INTO schools (id, ancestor_id) VALUES ('school 1', 'ancestor 1'), ('ancestor 1', NULL)
Я ожидал, что запрос insert вызовет исключение внешнего ключа, но на самом деле он успешно вставляет 2 строки.
Кто-нибудь знает, почему это так? Я просмотрел документ для вставки, но в нем не упоминаются какие-либо случаи, подобные этому.
Ответ №1:
Ограничения внешнего ключа реализуются с помощью триггеров в PostgreSQL. Это не задокументировано напрямую, но вы можете увидеть это косвенно здесь:
Можно отключить или включить один триггер, указанный по имени, или все триггеры в таблице, или только пользовательские триггеры (этот параметр исключает внутренние триггеры ограничений, такие как те, которые используются для реализации ограничений внешнего ключа или отложенных ограничений уникальности и исключения).
Вы также можете увидеть это с помощью
SELECT * FROM pg_trigger
WHERE tgrelid = 'schools'::regclass;
Правила запуска триггеров документированы и применяются также к внешним ключам:
BEFORE
Триггеры на уровне строк срабатывают непосредственно перед обработкой конкретной строки, в то время как триггеры на уровне строкAFTER
срабатывают в конце инструкции (но перед любымиAFTER
триггерами на уровне инструкций).
(Выделение мое)
Таким образом, внешние ключи проверяются после выполнения инструкции complete.
Комментарии:
1. спасибо @Laurenz за подробности. Теперь я знаю, что foreign key использует триггер для проверки ограничения. Но я не могу найти документ, в котором мы знаем, что триггер, реализованный для ограничения внешнего ключа, является уровнем AFTER. Вы случайно не знаете, где я могу это найти?
2. Это во второй цитате из документации. Я выделил соответствующую часть жирным шрифтом.
Ответ №2:
Внешний ключ вычисляется после завершения выполнения инструкции, а не после каждой строки.
Вот почему удаление их в одном операторе также работает:
delete from schools
where id in ('school 1', 'ancestor 1');