Внешние ключи и ссылочная целостность

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

Внешний ключ (FOREIGN KEY) — столбец, значения которого обязаны ссылаться на существующую строку в другой (или той же) таблице.

Зачем нужны внешние ключи

Представьте таблицы users и orders: у заказа есть user_id. Если ничто не следит за связью, можно создать заказ с user_id = 999, которого нет, — получится «висячая» ссылка. Внешний ключ запрещает такое: база проверяет, что user_id указывает на реальную строку в users. Это и есть ссылочная целостность.

Важно: внешние ключи работают только в InnoDB. В MyISAM объявление ключа молча игнорируется — ещё одна причина выбирать InnoDB.

Объявление внешнего ключа

CREATE TABLE orders (
  id INT AUTO_INCREMENT PRIMARY KEY,
  user_id INT NOT NULL,
  total DECIMAL(10,2),
  FOREIGN KEY (user_id) REFERENCES users(id)
) ENGINE=InnoDB;

ON DELETE и ON UPDATE

Что делать, если удаляют пользователя, у которого есть заказы? Поведение задаётся опциями:

ОпцияЧто происходит при удалении родителя
RESTRICT (по умолчанию)запретить удаление, пока есть ссылки
CASCADEудалить вместе с ним и связанные строки
SET NULLобнулить ссылку (столбец должен допускать NULL)
FOREIGN KEY (user_id) REFERENCES users(id)
  ON DELETE CASCADE
  ON UPDATE CASCADE

Демонстрация на песочнице

SQLite тоже понимает внешние ключи. Соберём связанные таблицы и сделаем выборку через JOIN — она покажет, как связь работает на чтении:

CREATE TABLE users (
  id INTEGER PRIMARY KEY,
  name TEXT
);

CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  user_id INTEGER,
  total INTEGER,
  FOREIGN KEY (user_id) REFERENCES users(id)
);

INSERT INTO users (name) VALUES ('Анна'), ('Борис');
INSERT INTO orders (user_id, total) VALUES (1, 500), (1, 300), (2, 700);

SELECT u.name, o.total
FROM orders o
JOIN users u ON u.id = o.user_id
ORDER BY u.name;

Вывод:

name   total
Анна   500
Анна   300
Борис  700

Нормализация одной фразой

Внешние ключи — техническая опора нормализации: вместо того чтобы дублировать имя пользователя в каждой строке заказов, мы храним его один раз в users, а в orders держим только ссылку user_id. Меньше дублей — меньше рассогласований.

Итог

  • Внешний ключ заставляет значение ссылаться на реальную строку в другой таблице.
  • Это обеспечивает ссылочную целостность — нет «висячих» ссылок.
  • Работает только в InnoDB; в MyISAM игнорируется.
  • ON DELETE CASCADE / SET NULL / RESTRICT задают поведение при удалении родителя.
Проверьте себя
1. Что гарантирует внешний ключ?
Aчто столбец уникален
Bчто значение ссылается на существующую строку в связанной таблице
Cчто столбец не пустой
Dчто данные зашифрованы
2. В каком движке MySQL работают внешние ключи?
AMyISAM
BMEMORY
CInnoDB
Dв любом одинаково
3. Что делает ON DELETE CASCADE?
Aзапрещает удаление родительской строки
Bобнуляет внешний ключ
Cничего не делает
Dпри удалении родителя удаляет и связанные дочерние строки