Что такое ON DELETE CASCADE в PostgreSQL?
Суть механизма
ON DELETE CASCADE — это часть определения ограничения FOREIGN KEY, которая указывает, что должно происходить со строками дочерней таблицы при удалении связанной строки родительской таблицы. Если задан CASCADE, PostgreSQL сам удалит все зависимые записи — не нужно вручную писать DELETE FROM child WHERE parent_id = ... перед удалением родителя.
Синтаксис
CREATE TABLE authors (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE books (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
author_id INTEGER REFERENCES authors(id) ON DELETE CASCADE
);
Если ограничение уже существует, его нужно пересоздать через ALTER TABLE, так как изменить действие ON DELETE на месте нельзя:
ALTER TABLE books DROP CONSTRAINT books_author_id_fkey;
ALTER TABLE books
ADD CONSTRAINT books_author_id_fkey
FOREIGN KEY (author_id) REFERENCES authors(id) ON DELETE CASCADE;
Другие варианты ON DELETE
NO ACTION(по умолчанию) — запрещает удаление, если есть ссылающиеся строки (проверка откладывается до конца оператора)RESTRICT— то же самое, но проверка немедленная, без возможности отложитьSET NULL— у дочерних строк FK-колонка обнуляетсяSET DEFAULT— FK-колонке присваивается значение по умолчаниюCASCADE— дочерние строки удаляются вместе с родителем
Как это работает изнутри
Каскадное удаление выполняется внутри той же транзакции, что и исходный DELETE. Технически это реализовано через внутренние триггеры, привязанные к ограничению внешнего ключа, а не через явные пользовательские триггеры. Если на любом шаге каскада возникает ошибка (например, срабатывает другое ограничение), вся транзакция атомарно откатывается — частично удалённых данных не остаётся.
Многоуровневый каскад
CASCADE применяется рекурсивно. Если таблица C ссылается на B с ON DELETE CASCADE, а B ссылается на A тоже с ON DELETE CASCADE, то удаление строки в A каскадно удалит связанные строки B, а вслед за ними — связанные строки C.
Риски и практика применения
CASCADE — операция, которая молча и необратимо удаляет данные, поэтому её стоит использовать осознанно:
- Для действительно строго зависимых сущностей (строки заказа без заказа не имеют смысла) CASCADE уместен
- Для важных данных, где случайное удаление дорого, чаще выбирают
RESTRICT/NO ACTIONи явное подтверждение на уровне приложения, либо soft delete (флагdeleted_at) вместо физического удаления - Когда связь необязательна, логичнее
SET NULL
Производительность
PostgreSQL не создаёт индекс на колонке внешнего ключа автоматически (в отличие от первичного ключа). Без индекса каскадное удаление вызывает полное сканирование дочерней таблицы при каждом удалении родителя, что на больших объёмах данных приводит к деградации и долгим блокировкам. Поэтому на FK-колонку почти всегда стоит добавлять индекс:
CREATE INDEX idx_books_author_id ON books(author_id);
Что хочет услышать интервьюер
Понимание, что CASCADE — это опция FOREIGN KEY, а не отдельная команда или триггер
Знание альтернатив: RESTRICT, NO ACTION, SET NULL, SET DEFAULT и разницу между ними
Понимание атомарности — каскад выполняется в той же транзакции
Осознание рисков: каскад может неожиданно удалить много связанных данных
Знание про отсутствие автоиндекса на FK-колонке и связанные проблемы производительности
Пример: Создание таблиц с ON DELETE CASCADE
CREATE TABLE authors (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE books (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
author_id INTEGER REFERENCES authors(id) ON DELETE CASCADE
);
-- при удалении автора все его книги удалятся автоматически
DELETE FROM authors WHERE id = 1;
Пример: Пересоздание ограничения через ALTER TABLE
ALTER TABLE books DROP CONSTRAINT books_author_id_fkey;
ALTER TABLE books
ADD CONSTRAINT books_author_id_fkey
FOREIGN KEY (author_id) REFERENCES authors(id) ON DELETE CASCADE;
-- индекс на FK-колонке ускоряет каскадные удаления
CREATE INDEX idx_books_author_id ON books(author_id);
Типичные ошибки
Путают ON DELETE CASCADE с обычным триггером или считают, что нужно писать его вручную
Не знают разницы между RESTRICT и NO ACTION (полагают, что это одно и то же)
Забывают, что многоуровневые связи каскадируются рекурсивно, и недооценивают масштаб удаления
Не упоминают, что PostgreSQL не индексирует FK-колонки автоматически, что делает каскадные удаления медленными
Считают CASCADE безопасным выбором по умолчанию, не обсуждая alternative вроде soft delete


