JavaRush /Курсы /SQL SELF /Использование ON DELETE CASCADE и ON...

Использование ON DELETE CASCADE и ON UPDATE RESTRICT

SQL SELF
19 уровень , 2 лекция
Открыта

Представьте, что вы создали базу данных, где есть таблица клиентов (customers) и связанные с ними заказы (orders). Однако в какой-то момент возникает задача: что делать, если клиент удаляется из таблицы customers? Должны ли заказы этого клиента тоже удаляться, или они останутся "сиротами", ссылаясь на несуществующего клиента? А что, если вы решите изменить ID клиента? Вот где на арену выходят каскадные операции (CASCADE) и ограничения (RESTRICT) для управления поведением базы данных.

ON DELETE CASCADE — это механизм, который автоматически удаляет связанные записи при удалении записи из родительской таблицы. Другими словами, если вы удалили клиента, то все связанные с ним заказы тоже удалятся.

Это работает следующим образом. Когда вы добавляете ON DELETE CASCADE в определение внешнего ключа, база данных "понимает", что связанная запись должна быть удалена автоматически.

Пример

Допустим, у нас есть две таблицы: customers и orders. Клиенты customers могут сделать несколько заказов orders, что соответствует связи ONE-TO-MANY. Мы хотим, чтобы при удалении клиента все его заказы также удалялись.

-- Создаем таблицу клиентов
CREATE TABLE customers (
    customer_id SERIAL PRIMARY KEY,
    name TEXT NOT NULL
);

-- Создаем таблицу заказов с внешним ключом, ссылающимся на таблицу клиентов
CREATE TABLE orders (
    order_id SERIAL PRIMARY KEY,
    customer_id INT REFERENCES customers(customer_id) ON DELETE CASCADE,
    order_date DATE NOT NULL
);

Вставим данные в таблицы

-- Вставляем данные в таблицу клиентов
INSERT INTO customers (name) VALUES ('Иван'), ('Анна');

-- Вставляем данные в таблицу заказов
INSERT INTO orders (customer_id, order_date) VALUES
(1, '2023-10-01'),
(1, '2023-10-02'),
(2, '2023-10-03');

Таблица orders:

order_id customer_id order_date
1 1 2023-10-01
2 1 2023-10-02
3 2 2023-10-03

Удаляем клиента и проверяем, что произойдет

-- Удаляем клиента с ID 1
DELETE FROM customers WHERE customer_id = 1;

-- Проверяем, что осталось в таблице заказов
SELECT * FROM orders;
order_id customer_id order_date
3 2 2023-10-03

Как видим, заказы, связанные с удаленным клиентом, также были удалены.

Ограничение изменений: ON UPDATE RESTRICT

ON UPDATE RESTRICT позволяет предотвратить изменение значения в родительской таблице, если запись в дочерней таблице ссылается на это значение. Это своеобразный "защитный барьер", который предотвращает изменения, способные нарушить целостность данных.

Как это работает? Когда вы добавляете ON UPDATE RESTRICT, база данных не позволит обновить ключ в родительской таблице, если на него ссылаются записи в дочерней таблице.

Пример

Мы берем всё те же таблицы customers и orders, но добавим к внешнему ключу ограничение изменения.

-- Пересоздаем таблицу заказов с ограничением на обновление
DROP TABLE orders;

CREATE TABLE orders (
    order_id SERIAL PRIMARY KEY,
    customer_id INT REFERENCES customers(customer_id) ON UPDATE RESTRICT,
    order_date DATE NOT NULL
);

Попробуем обновить ID клиента

-- Попытка изменить ID клиента с 2 на 5
UPDATE customers 
SET customer_id = 5 
WHERE customer_id = 2;

Результат:

ERROR:  update or delete on table "customers" violates foreign key constraint
DETAIL:  Key (customer_id)=(2) is still referenced from table "orders".

Как видите, база данных выбросила ошибку, так как изменение ключа нарушило бы связь между таблицами.

Подробнее про UPDATE и нюансы его работы я расскажу в следующем уровне :P

Совмещение ON DELETE CASCADE и ON UPDATE RESTRICT

Разумеется, каскадные операции (CASCADE) и ограничения (RESTRICT) можно сочетать. Например, вы можете настроить автоматическое удаление связанных данных при удалении родительской записи (ON DELETE CASCADE), но при этом запретить изменение её ID (ON UPDATE RESTRICT), чтобы избежать нежелательных последствий.

Пример

Создадим ещё раз таблицу заказов, используя оба механизма:

DROP TABLE orders;

CREATE TABLE orders (
    order_id SERIAL PRIMARY KEY,
    customer_id INT REFERENCES customers(customer_id)
        ON DELETE CASCADE
        ON UPDATE RESTRICT,
    order_date DATE NOT NULL
);

Теперь:

  • Если вы удалите клиента, все его заказы будут удалены.
  • Если вы попытаетесь изменить ID клиента, получите ошибку.

Почему это важно в реальных проектах?

Использование CASCADE и RESTRICT очень важно для крупных систем с большим количеством связанных таблиц. Например:

В интернет-магазине клиент может иметь заказы. Если клиент решит удалить свой профиль, вы не хотите оставлять в базе данных заказы, которые уже ни на что не ссылаются. Здесь на помощь придет ON DELETE CASCADE.

В то же время, вы можете захотеть предотвратить случайные изменения уникальных ключей, чтобы не разрушить связи между таблицами. Для этого пригодится ON UPDATE RESTRICT.

2
Задача
SQL SELF, 19 уровень, 2 лекция
Недоступна
Создание таблиц с `ON DELETE CASCADE`
Создание таблиц с `ON DELETE CASCADE`
2
Задача
SQL SELF, 19 уровень, 2 лекция
Недоступна
Добавление ограничения `ON UPDATE RESTRICT`
Добавление ограничения `ON UPDATE RESTRICT`
Комментарии (6)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Slevin Уровень 9
13 сентября 2025
Вторая задача оформлена сумасшедшим и валидируется им же.
Ra Уровень 35 Student
30 июля 2025
По моему в теорию надо добавить зачем нужен CONSTRAINT fk_author FOREIGN KEY (author_id) REFERENCES authors (author_id) ON DELETE CASCADE -- Удаление книг при удалении автора ON UPDATE RESTRICT если можно через references всё это записать. Типа ограничение можно удалить/изменить ALTER TABLE books DROP CONSTRAINT fk_author; , а с references нет
Евгений Уровень 49 Expert
6 августа 2025
REFERENCES также создаёт ограничение, которое может быть удалено. Например, вот этот запрос:

create table poops_details
(
    poop_id                INTEGER NOT NULL REFERENCES poops (id),
    additional_description TEXT    NOT NULL
);
Создаёт ограничение poops_details_poop_id_fkey, которое можно точно так же удалить. Согласно этому ответу: https://stackoverflow.com/a/12315244/14614707 единственное преимущество явного объявления FOREIGN KEY в том, что с его помощью можно создавать составные внешние ключи (ключи, состоящие из нескольких колонок). Ну и я предпочитаю в работе использовать FOREIGN KEY, потому что все ссылки и все ограничения я пишу в самом конце выражения CREATE TABLE, после колонок, и я сразу знаю, где мне эти ограничения и правила в случае чего искать 😏
Ra Уровень 35 Student
7 августа 2025
Интересно, а как вообще на проде пишется SQL, через Ликвибейз какой-нибудь? Ещё, а вы используете JSONB?
Евгений Уровень 49 Expert
7 августа 2025
Да, на проде я сталкивался с flyway или liquebase (flyway немного проще), но запросы от этого не меняются. jsonb конкретно я стараюсь избегать, но он используется. У него два преимущества: проще создавать таблицы и проще работать со структурами, у которых постоянно разный набор полей, но я не так часто нуждался в подобном. ну и разбираться потом сложнее с таблицами. Да, один раз я писал небольшой сервис, который просто сохранял json и потом отдавал его по требованию. в данном случае jsonb идеально подходит, ну по сути можно было просто text использовать, потому что я никаких операций с этимм данными не делал)
Ra Уровень 35 Student
7 августа 2025
Спасибо 👍