Модуль 19

Каскадні операції та orphan records

Ось готовий урок, створений за твоїм майстер-промптом.


🏛️ CS50: Каскадні операції та Orphan Records

(Уявіть, що ми в лекційній залі Sanders Theatre. Я ходжу сценою, закочую рукави сорочки та звертаюсь до вас.)

1. 🔥 Вступ: Коли зникає фундамент

Уявіть, що ви будуєте хмарочос. У вас є перший поверх, на ньому — другий, а там — третій. Все тримається купи. А тепер уявіть, що я приходжу з динамітом і підриваю лише перший поверх.

Що станеться з другим і третім? Вони зависнуть у повітрі, як у грі Minecraft? Чи з гуркотом обваляться додолу?

У світі баз даних це не просто фізика, це — логіка вашого додатку.

Давайте перенесемось у реальний світ. Ви розробляєте Instagram. Користувач вирішує видалити свій акаунт. Натискає "Delete". Але у нього є 1000 фотографій, 5000 коментарів і 200 лайків.

Риторичне питання: Що має статися з цими фотографіями? 1. Вони мають зникнути разом з користувачем? 2. Чи вони мають залишитись висіти в базі даних, але без власника — як привиди?

Якщо ви оберете другий варіант, вітаю — ви створили Orphan Records (записи-сироти). Це дані, які нікому не належать, займають місце і можуть зламати ваш код, коли ви спробуєте дізнатися, "хто ж запостив це фото".

Сьогодні ми розберемося, як не перетворити вашу базу даних на цвинтар забутих записів. Ми поговоримо про Каскадні операції.


2. 🧠 Теоретична база (Як це працює під капотом)

У реляційних базах даних (SQL) є "священне правило": Referential Integrity (Цілісність посилань).

База даних — це дуже суворий бібліотекар. Вона каже: "Ти не можеш покласти книгу на полицю, якої не існує". Так само: "Ти не можеш створити коментар від користувача, якого не існує".

Але що робити, коли користувач вже існував, а потім ми його видаляємо? Тут вступають у гру Foreign Keys (Зовнішні ключі) з особливими інструкціями.

Ось три сценарії, які ви повинні розуміти (інтуїтивно, як світлофор):

  1. 🔴 RESTRICT (Заборона) — Це стандартна поведінка.

    • Логіка: "Ти хочеш видалити користувача? Стоп! У нього є фотографії. Спочатку видали фотографії, і тільки потім я дозволю видалити користувача".
    • Аналогія: Ви не можете знести стіну, поки не знімете з неї картини.
  2. 🟢 CASCADE (Ланцюгова реакція) — Це тема нашого уроку.

    • Логіка: "Ти видаляєш користувача? Окей, я автоматично знайду і знищу всі його фото, коментарі та лайки. Все, що було прив'язане до нього — зникне".
    • Аналогія: Ви спалюєте щоденник — зникають і сторінки всередині.
  3. 🟡 SET NULL (Вільне плавання).

    • Логіка: "Ти видаляєш користувача? Добре. Фотографії я залишу, але в графі 'автор' я зітру ім'я і напишу NULL (пусто)".
    • Аналогія: Автор книги помер, але книга залишилась у бібліотеці. Вона тепер "народна".

Запам'ятайте головне:

Orphan Record — це запис (дитина), який посилається на батьківський запис, якого вже не існує. Це сміття. Каскадні операції — це спосіб автоматично прибирати це сміття або керувати ним.


3. 🧪 Приклади (Від простого до реального)

Давайте подивимось на код. Уявіть просту систему замовлень піци.

Приклад 1: Проблема (RESTRICT)

-- Створюємо клієнтів
CREATE TABLE customers (
    id INT PRIMARY KEY,
    name VARCHAR(50)
);

-- Створюємо замовлення
CREATE TABLE orders (
    id INT PRIMARY KEY,
    customer_id INT,
    pizza VARCHAR(50),
    FOREIGN KEY (customer_id) REFERENCES customers(id) -- Звичайний зв'язок
);

INSERT INTO customers VALUES (1, 'David');
INSERT INTO orders VALUES (101, 1, 'Pepperoni');

А тепер я, як адміністратор, намагаюся видалити Девіда:

DELETE FROM customers WHERE id = 1;

Питання до вас: Чи виконається ця команда? . . . Відповідь: Ні! База видасть помилку. Вона скаже: Foreign key constraint fails. Вона захищає замовлення 101 від осиротіння.


Приклад 2: Рішення (ON DELETE CASCADE)

Тепер змінимо правила гри. Ми хочемо, щоб якщо клієнт видаляється, історія його замовлень теж зникала (наприклад, для дотримання GDPR — права на забуття).

CREATE TABLE orders (
    id INT PRIMARY KEY,
    customer_id INT,
    pizza VARCHAR(50),
    -- Додаємо магічну фразу:
    FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE CASCADE
);

Тепер, якщо ми виконаємо:

DELETE FROM customers WHERE id = 1;

Результат: 1. Клієнт David зникне. 2. Замовлення 101 ('Pepperoni') теж зникне автоматично. База даних сама зробила "прибирання". Ніяких сиріт.


Приклад 3: Альтернатива (ON DELETE SET NULL)

Уявіть, що ви менеджер складу. Вам байдуже, що клієнт видалився, вам треба знати, що піцу 'Pepperoni' було продано, щоб звести дебет з кредитом. Видаляти замовлення не можна!

CREATE TABLE orders (
    id INT PRIMARY KEY,
    customer_id INT, -- Важливо: це поле має дозволяти NULL
    pizza VARCHAR(50),
    FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE SET NULL
);

Видаляємо Девіда (DELETE FROM customers...).

Результат: Клієнта немає. Замовлення 101 залишилось, але customer_id тепер дорівнює NULL. Це "Анонімне замовлення".


4. 🛠 Практична частина

Час "забруднити руки". Відкрийте ваш термінал або SQL-редактор (або уявіть це).

Завдання 1: Створення світу Створіть дві таблиці: Authors (Автори) і Books (Книги). Один автор може мати багато книг. Використайте ON DELETE CASCADE.

Завдання 2: Тест на руйнування Додайте автора "Taras Shevchenko" і книгу "Kobzar". Видаліть Тараса. Перевірте таблицю Books. Чи порожня вона? (Має бути порожня).

Завдання 3: Зміна логіки Перестворіть таблицю Books, але тепер використайте ON DELETE RESTRICT (або просто приберіть CASCADE). Спробуйте видалити автора, у якого є книги. Прочитайте помилку. Зрозумійте її.

Завдання 4: Пошук сиріт (Orphan Hunting) Уявіть, що вам дісталася стара база даних, де забули додати зовнішні ключі. Напишіть запит, який знайде всі книги, у яких author_id посилається на ID, якого не існує в таблиці Authors. (Підказка: використовуйте LEFT JOIN де Authors.id IS NULL).

Завдання 5: Реальний кейс Ви робите "Кошик" (Cart) в інтернет-магазині. Кошик належить Користувачу. Що логічніше використати для Кошика при видаленні Користувача: CASCADE чи SET NULL? Чому?

Бонусне питання ("А що, якщо..."): Що станеться, якщо у вас ланцюжок: Grandparent -> Parent -> Child, і скрізь стоїть CASCADE? Якщо видалити Grandparent, чи зникне Child?


5. 💡 Мислення як у розробника

Ви зараз можете подумати: "О, CASCADE — це супер зручно! Я буду ставити його всюди!"

Стоп. Досвідчений розробник тут скаже: "Обережно з червоною кнопкою".

  1. Небезпека масового знищення: Уявіть, що ви випадково видалили запис "Категорія: Електроніка". Якщо у вас стоїть CASCADE, ви за мілісекунду знищите всі товари вашого магазину, всі відгуки до них і всі історії замовлень. Ваш бізнес перестане існувати за один клік.

  2. Soft Deletes (М'яке видалення): У професійних системах (Facebook, банківські системи) ми рідко робимо справжній DELETE. Ми додаємо колонку is_deleted (true/false).

    • Як думає профі: "Дані — це нафта. Не спалюй їх. Просто сховай".
    • CASCADE частіше використовують для тимчасових даних (наприклад, сесії користувача), а не для бізнес-даних.
  3. Orphans — це витік пам'яті в БД: Якщо ви не налаштували зв'язки (Foreign Keys) і видаляєте дані вручну, у вас накопичуються гігабайти "сміття". Це сповільнює базу. Завжди думайте: "Хто прибере за мною?"


6. 🧩 Підсумок

Отже, що ми маємо у сухому залишку?

  • Ми дізналися, що Orphan Records — це діти, які загубили батьків у базі даних.
  • Ми зрозуміли, що CASCADE — це безжальний кілер, який підчищає все зв'язане.
  • Ми розібрали SET NULL — як спосіб зберегти історію, але розірвати зв'язок.
  • І найголовніше: тепер ви розумієте, що видалення одного запису може спричинити ефект метелика для всієї системи.

Тепер ви вмієте: Проєктувати зв'язки так, щоб ваша база даних залишалася чистою і логічною, навіть коли дані знищуються.

Далі у курсі: Гаразд, ми навчилися видаляти. Але що, якщо під час видалення вимкнеться світло? Чи видалиться половина даних? Наступного разу ми поговоримо про Транзакції (ACID) — як зробити так, щоб "все або нічого".

А поки що... це був CS50!