Пошук уроків, статей та іншого контенту
PRIMARY KEY, FOREIGN KEY, UNIQUE, NOT NULL, CHECK — правила, які база даних не дозволить порушити.
Валідацію даних можна (і потрібно, per стандартну практику проєкту) робити на рівні застосунку — але код застосунку не єдиний спосіб, яким дані можуть потрапити в базу: пряма правка через адмінський SQL-клієнт, скрипт міграції, баг в іншому сервісі, що пише в ту саму базу даних. Constraints — правила цілісності, що діють на рівні самої бази даних, незалежно від того, який саме код намагається записати дані.
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email TEXT NOT NULL UNIQUE, -- ніколи NULL, і ніколи не повторюється серед усіх рядків
name TEXT NOT NULL
);Зовнішній ключ гарантує, що значення стовпця обов'язково відповідає реально існуючому рядку іншої таблиці — неможливо створити замовлення з user_id, якого немає серед users:
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id),
total NUMERIC(10, 2) NOT NULL
);Коли рядок, на який посилається зовнішній ключ, видаляють, потрібно явно визначити поведінку для пов'язаних рядків:
ON DELETE CASCADE — видалити й усі пов'язані рядки автоматично (видалення користувача видаляє всі його замовлення).
ON DELETE RESTRICT (типова поведінка за замовчуванням) — заборонити видалення користувача, поки існують пов'язані замовлення.
ON DELETE SET NULL — обнулити зовнішній ключ у пов'язаних рядках (замовлення лишається, але без прив'язки до видаленого користувача) — вимагає, щоб стовпець допускав NULL.
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADECHECK перевіряє довільний булевий вираз на кожен рядок перед записом:
price NUMERIC(10, 2) NOT NULL CHECK (price >= 0)ON DELETE CASCADE зручний, але небезпечний без обдуманого рішення — випадкове видалення одного «кореневого» рядка може каскадно видалити величезну кількість пов'язаних даних в інших таблицях. Для даних, які краще зберігати навіть після «видалення» пов'язаного запису (історичні замовлення, аудит-логи), SET NULL чи м'яке видалення (позначення deleted_at замість реального DELETE) часто безпечніші.
Покладатись лише на валідацію застосунку й не додавати відповідні constraints на рівні бази даних — залишає можливість пошкодити цілісність даних будь-яким кодом чи процесом, що звертається до бази даних в обхід цієї конкретної валідації.
ON DELETE CASCADE без свідомого рішення про це — випадкове видалення батьківського рядка може непомітно каскадно знищити значно більше даних, ніж очікувалось.
NOT NULL там, де UNIQUE насправді потрібен, і навпаки — NOT NULL забороняє відсутність значення, UNIQUE забороняє повторення значення; це різні, часто обидва потрібні одночасно (як з email вище) правила.
Constraints (PRIMARY KEY, FOREIGN KEY, UNIQUE, NOT NULL, CHECK) — правила цілісності даних, які застосовує сама база даних, незалежно від того, який код намагається записати дані, доповнюючи, а не замінюючи валідацію на рівні застосунку. ON DELETE визначає, що станеться з пов'язаними рядками при видаленні — CASCADE, RESTRICT чи SET NULL — і заслуговує свідомого, а не типового вибору.