Външни ключове: връзки между таблиците
Накратко: Външният ключ (REFERENCES) пази връзката между две таблици, а ON DELETE казва какво става със свързаните редове при изтриване: CASCADE, SET NULL или RESTRICT.
Проблем
Изтрий ми профила
Мария Иванова пише: „Моля, изтрийте профила ми.“ Опитваш:
Базата отказва. Мария има 12 поръчки, а всяка от тях сочи към нея. Ако клиентът изчезне, поръчките ще сочат към никого.
Какво трябва да стане с поръчките ѝ? А с отзивите ѝ? А със списъка ѝ с любими продукти? Отговорът е различен за всяка таблица. И се записва в схемата.
Идеята
REFERENCES
Външният ключ вече го познаваш: колона, която сочи към ред в друга таблица. В CREATE TABLE се пише с REFERENCES таблица(колона). Така са направени поръчките на магазина:
orders
- PK
idinteger - FK
customer_idinteger *→ customers ordered_attext *statustext *
Ето нова таблица: любимите продукти на всеки клиент. Първият ред влиза, вторият сочи към клиент 99, който го няма:
Сега базата сама пази връзките. Ред, който сочи към несъществуващ клиент или продукт, не влиза.
Пробвай
Любимите продукти
Таблицата wishlist вече има колоните си. Направи customer_id да сочи към customers, а product_id към products.
Решение
CREATE TABLE wishlist (
customer_id INTEGER NOT NULL REFERENCES customers(id),
product_id INTEGER NOT NULL REFERENCES products(id),
PRIMARY KEY (customer_id, product_id)
);customer_id INTEGER NOT NULL REFERENCES customers(id) и product_id INTEGER NOT NULL REFERENCES products(id). Ако след името на таблицата не пишеш колона, SQLite приема, че сочиш към първичния ѝ ключ.
Идеята
ON DELETE CASCADE
След REFERENCES може да кажеш какво да стане, когато редът, към който сочиш, се изтрие. Това е ON DELETE.
CASCADE значи „изтрий и мен“. Явор Пенев (клиент 20) има два любими продукта. Теодора Генова (клиент 19) има един. Явор изтрива профила си:
Любимите на Явор изчезнаха с него. Тези на Теодора са си там. Списък с любими без клиента няма смисъл, затова тук CASCADE е правилно.
Идеята
SET NULL и RESTRICT
SET NULL значи „остави ме, но без връзка“. Въпросите към продуктите са полезни и за другите купувачи, дори когато авторът си е тръгнал:
Въпросът остана, а customer_id стана NULL: въпрос от бивш клиент.
RESTRICT значи „не пускай изтриването“. Фактурите трябва да се пазят години наред по закон:
Без ON DELETE поведението е почти същото: изтриването спира. Затова Мария не можа да изтрие профила си. Поръчките са история на магазина и не бива да изчезват.
По-точно, за любопитните
Без ON DELETE правилото е NO ACTION. Разликата с RESTRICT е само кога се проверява: RESTRICT спира веднага, а NO ACTION проверява в края на заявката (или на транзакцията, ако проверката е отложена). Има и ON UPDATE за промяна на ключа, но се ползва рядко, защото първичният ключ почти никога не се сменя. ON DELETE SET DEFAULT също е рядкост.
Виж го
SET NULL срещу NOT NULL
Някой е написал таблицата за въпроси така:
customer_id INTEGER NOT NULL REFERENCES customers(id) ON DELETE SET NULL
Явор пак изтрива профила си:
SET NULL иска да запише NULL, а NOT NULL не го пуска. Изтриването спира. SQLite не предупреждава за това при CREATE TABLE, а чак когато някой се опита да изтрие. Колона със SET NULL трябва да може да е празна.
Пробвай
Адресите за доставка
Таблицата addresses пази адресите на клиентите. Когато клиентът изтрие профила си, адресите му трябва да изчезнат заедно с него. Поправи customer_id.
Решение
CREATE TABLE addresses (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
city TEXT NOT NULL,
street TEXT NOT NULL
);REFERENCES customers(id) ON DELETE CASCADE. Адрес без клиент не трябва на никого, а и това са лични данни, които не бива да остават.
Идеята
Проверката трябва да е включена
В курса базата проверява външните ключове. Питаш я с PRAGMA, командата за настройките на SQLite:
1 значи „включено“. Ако я изключиш, REFERENCES вече не пази нищо:
Поръчка на клиент 99, който го няма, влезе без грешка.
По-точно, за любопитните
В истинския SQLite проверката е изключена по подразбиране. Пусни sqlite3 на своя компютър и напиши PRAGMA foreign_keys;: отговорът е 0. Причината е старите програми, писани преди SQLite да проверява външни ключове. Настройката важи само за една връзка с базата. Затова програмата пуска PRAGMA foreign_keys = ON; всеки път, когато отваря базата. Тази страница го прави вместо теб. PostgreSQL, MySQL (с InnoDB) и SQL Server проверяват външните ключове по подразбиране.
Упражнение
Въпросите към продуктите
Таблицата questions има id, product_id, customer_id и body. Когато продуктът се махне от магазина, въпросите за него изчезват. Когато клиентът изтрие профила си, въпросите му остават, но без автор. Добави връзките.
Решение
CREATE TABLE questions (
id INTEGER PRIMARY KEY,
product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE,
customer_id INTEGER REFERENCES customers(id) ON DELETE SET NULL,
body TEXT NOT NULL
);product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE и customer_id INTEGER REFERENCES customers(id) ON DELETE SET NULL. customer_id е без NOT NULL, иначе SET NULL няма да може да работи.
Тест
Провери се
Свързани уроци
Раздел „Свързани уроци“- Нужно отпреди: INSERT: нови редове · DELETE: изтриване на редове · CREATE TABLE: нова таблица · Ограничения: правила за данните
- Продължава в: ALTER TABLE: промяна на таблица · Видове връзки: 1:1, 1:N, M:N · ER диаграма: от описание до схема · Нормализация
- Задачи по темата: Таблици и правила