Модуль 8

Constraints and Data Integrity

Ось готовий урок, створений за твоїм майстер-промптом. Я намагався передати енергію, структуру та стиль подачі Девіда Малана, адаптувавши це для української аудиторії.


🎓 CS50: Вартові твоїх даних (Constraints & Data Integrity)

Привіт, друзі! Це CS50, і сьогодні ми поговоримо про те, що відрізняє професійну базу даних від звичайного текстового файлу, в якому панує хаос.

Сьогоднішня тема — Constraints (Обмеження) та Data Integrity (Цілісність даних).


1. 🔥 Вступ: Чому не можна довіряти нікому?

Уявіть, що ви відкриваєте новий банк. Ви наймаєте касира, назвемо його, скажімо, Джон. І ось приходить перший клієнт, щоб відкрити рахунок. Джон бере анкету і... забуває записати ім'я клієнта. Або в полі "Баланс" пише: "-1000 доларів" (бо клієнт йому не сподобався). Або ще гірше — видає двом різним людям абсолютно однаковий номер рахунку.

Запитання до вас: Що станеться з вашим банком через тиждень? (Пауза для роздумів) Правильно. Хаос. Судові позови. Банкрутство.

У світі баз даних ми називаємо це "Garbage In, Garbage Out" (Сміття на вході — сміття на виході).

Якщо ви дозволите своїй програмі записувати в базу будь-що, вона це зробить. Вона запише користувача без email-у, товар з від'ємною ціною або замовлення для клієнта, якого не існує.

Навіщо нам Constraints (Обмеження)? Щоб перетворити базу даних з "смітника" на фортецю. База даних має бути останнім рубежем оборони. Навіть якщо ваш фронтенд зламався, навіть якщо бекенд-розробник (можливо, це ви о 3-й ночі) припустився помилки — база даних має сказати: "Стоп! Я це не збережу, бо це порушує закони логіки".


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

Давайте розберемося з інструментами, які у нас є. Уявіть, що ви — фейс-контроль у нічному клубі (вашій таблиці). У вас є чіткі інструкції, кого пускати, а кого — ні.

Ось ваші головні правила (Constraints):

1. NOT NULL (Не порожнє)

Це просто. Чи може існувати користувач без імені? Ні. * Логіка: Це поле обов'язкове для заповнення. * Аналогія: Ви не можете отримати посилку, якщо не вкажете адресу доставки.

2. UNIQUE (Унікальність)

Чи можуть два користувачі мати однаковий email? Ні, бо як ми їх розрізнимо під час входу? * Логіка: Значення в цьому стовпчику не можуть повторюватися.

3. PRIMARY KEY (Первинний ключ)

Це Святий Грааль вашої таблиці. Це унікальний ідентифікатор кожного рядка (зазвичай id). * Як це працює: Це комбінація NOT NULL + UNIQUE. У кожного рядка має бути паспорт, і він має бути унікальним. * Запам'ятати: У кожної таблиці має бути Primary Key. Крапка.

4. CHECK (Перевірка умов)

Тут ми вмикаємо логіку. * Приклад: Ціна товару > 0. Вік користувача >= 18. * Логіка: Це "іфчик" (if statement), який живе прямо в базі даних.

5. FOREIGN KEY (Зовнішній ключ) — Найважливіше!

Це міст між таблицями. Уявіть таблицю Orders (Замовлення) і Users (Користувачі). Замовлення має знати, хто його зробив. Ми записуємо user_id у таблицю замовлень. * Що робить Foreign Key: Він гарантує, що ви не зможете створити замовлення для користувача з id=999, якщо такого користувача не існує в таблиці Users. Він також не дасть видалити користувача, якщо у нього є активні замовлення (щоб замовлення не стали "сиротами"). * Термін: Це називається Referential Integrity (Посилальна цілісність).


3. 🧪 Приклади (від хаосу до порядку)

Ми будемо використовувати SQL (PostgreSQL/MySQL стиль), але логіка однакова всюди.

Сценарій 1: Хаос (Без обмежень)

Створимо таблицю для інтернет-магазину.

CREATE TABLE products (
    id INT,
    name VARCHAR(100),
    price DECIMAL,
    sku VARCHAR(50) -- артикул товару
);

Що тут не так? Давайте спробуємо зламати систему:

INSERT INTO products (id, name, price, sku) VALUES (1, NULL, -50, 'ABC-123');
INSERT INTO products (id, name, price, sku) VALUES (1, 'iPhone', 1000, 'ABC-123');

Результат: База це "з’їла". 1. У нас два товари з id=1. Як ми їх оновимо? 2. У першого товару немає назви (NULL). 3. Перший товар коштує -50. Ми будемо доплачувати покупцю? 4. Артикул ABC-123 дублюється.

Це катастрофа.


Сценарій 2: Порядок (Додаємо Constraints)

Давайте перепишемо це як професіонали.

Чого ми очікуємо? Ми хочемо, щоб база даних видавала помилку, якщо ми спробуємо вставити нісенітницю.

CREATE TABLE products (
    id INT PRIMARY KEY,           -- Унікальний, не null
    name VARCHAR(100) NOT NULL,   -- Обов'язкове ім'я
    price DECIMAL CHECK (price > 0), -- Тільки позитивна ціна
    sku VARCHAR(50) UNIQUE        -- Унікальний артикул
);

Тепер спробуємо ті ж самі вставки.

  1. INSERT INTO products ... VALUES (1, NULL, ...) -> ❌ Error: Column 'name' cannot be null.
  2. INSERT INTO products ... VALUES (..., -50, ...) -> ❌ Error: Check constraint failed.
  3. Вставимо нормальний товар: INSERT INTO products VALUES (1, 'iPhone', 1000, 'ABC-123'); -> ✅ Success!
  4. Спробуємо вставити інший товар з таким самим SKU: INSERT INTO products VALUES (2, 'Samsung', 900, 'ABC-123'); -> ❌ Error: Duplicate entry for key 'sku'.

Бачите? База даних захищає вас від ваших же помилок!


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

Час "забруднити руки". Якщо у вас є доступ до SQL-терміналу або онлайн-редактора (наприклад, SQLFiddle), спробуйте це зараз.

Завдання 1: Створіть таблицю users * Поля: id (primary key), username (текст), age (число). * Умови: Ім'я має бути унікальним і не порожнім. Вік має бути 18 або більше.

Завдання 2: Тест на міцність * Спробуйте додати користувача без імені. * Спробуйте додати 17-річного. * Що вам відповіла база даних?

Завдання 3: Робота з Foreign Key (Зірочка ⭐) У вас є таблиця users. Створіть таблицю posts (дописи блогу). * У таблиці posts має бути поле user_id. * Зробіть це поле Foreign Key, яке посилається на users(id). * Питання: Що станеться, якщо ви спробуєте додати пост для user_id = 999, якого немає в таблиці users?

Завдання 4: Міні-кейс "Служба таксі" Придумайте структуру таблиці rides (поїздки). Які обмеження ви б додали до полів: * status (може бути лише: 'started', 'completed', 'cancelled')? * driver_rating (від 1 до 5)?


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

Ви можете запитати: "Девід, навіщо мені це прописувати в базі? Я ж можу перевірити це в коді на Python/JavaScript перед збереженням!"

Це типова помилка новачків. Ось як думає досвідчений архітектор (Senior Developer):

  1. Принцип "Глибинного захисту" (Defense in Depth): Ваш JavaScript код на фронтенді можуть обійти хакери. Ваш Python код на бекенді може мати баг. Але база даних — це фундамент. Якщо дані зіпсуються там, виправити це буде майже неможливо.
  2. Дані живуть довше за код: Сьогодні ваш сайт на PHP, завтра ви переписуєте його на Node.js, післязавтра — мобільний додаток на Swift. Всі вони працюють з однією базою. Якщо правила (Constraints) "вшиті" в базу, вам не треба дублювати логіку перевірки в кожному новому додатку.
  3. Порада з практики: Завжди давайте імена своїм обмеженням (constraints). Замість просто CHECK (price > 0), пишіть CONSTRAINT check_price_positive CHECK (price > 0). Коли через рік ви отримаєте помилку, повідомлення "Error: check_price_positive failed" скаже вам набагато більше, ніж просто "Error: constraint failed".

6. 🧩 Підсумок

Отже, що ми сьогодні поклали до свого багажу знань:

  1. Дані мають бути цілісними. Без цього ваш додаток — картковий будинок.
  2. Constraints — це ваші найкращі друзі. NOT NULL, UNIQUE, PRIMARY KEY, FOREIGN KEY, CHECK.
  3. Foreign Key тримає зв'язок між таблицями і не дозволяє з'являтися даним-"сиротам".

Що ви тепер вмієте? Ви вмієте проектувати таблиці, які неможливо зламати "сміттєвими" даними. Ви будуєте надійний фундамент.

Що далі? Тепер, коли наші дані надійно захищені і структуровані, виникає нове питання: "А що, якщо у нас мільйон користувачів? Як знайти потрібного за 0.01 секунди, а не гортати всю книгу?" На наступному уроці ми поговоримо про магію швидкості — Індекси (Indexes).

Це був CS50. Побачимось!