Ответ
Строго обязательно в подавляющем большинстве случаев. Индексы для внешних ключей критически важны для производительности операций, связанных с целостностью данных и соединениями таблиц.
Без индекса на столбце с внешним ключом:
- Проверка ограничений (CONSTRAINT) при операциях
INSERT/UPDATEв дочерней таблице потребует полного сканирования (FULL SCAN) родительской таблицы для каждого ряда. - Операции
DELETE/UPDATEв родительской таблице (особенно с каскадными действиямиCASCADE) будут выполняться крайне медленно, так как СУБД не сможет быстро найти зависимые строки в дочерней. - Запросы с
JOINпо этому ключу будут неэффективными.
Пример создания (MySQL/PostgreSQL):
-- Индекс часто создается автоматически при объявлении FOREIGN KEY.
-- Но лучше явно убедиться в его наличии.
ALTER TABLE `order_items`
ADD INDEX `idx_order_id` (`order_id`),
ADD CONSTRAINT `fk_order_items_orders`
FOREIGN KEY (`order_id`) REFERENCES `orders`(`id`)
ON DELETE CASCADE;
Исключения, когда индекс может быть избыточным, крайне редки:
- Таблица-справочник с очень малым и фиксированным количеством строк (например,
statusesс 5 записями). - Таблица, в которую только пишут (лог-таблица) и никогда не читают с JOIN или WHERE по этому ключу (маловероятный сценарий).
Правило простое: внешний ключ → индекс.
Ответ 18+ 🔞
Давай разберём эту тему, а то тут некоторые думают, что можно на авось. Смотри, если у тебя есть внешний ключ, а индекса на нём нет — это пиздец рубит производительность в ноль, я не шучу.
В 99.9% случаев — ставь, блядь, индекс. Это не обсуждается. Иначе ты сам себе злобный буратино.
Представь, что будет без индекса:
- Каждый раз, когда ты вставляешь новую запись в дочернюю таблицу (например,
order_items), база данных, чтобы проверить, а есть ли такойorder_idв родительской таблицеorders, вынуждена делать полный перебор всей таблицыorders. Это, ёпта,FULL SCAN. Если заказов миллион — она будет этот миллион просматривать для каждой новой позиции в заказе. Ебать колотить, какой кошмар. - Хуже того, если ты удаляешь заказ из
orders(особенно с каскадомON DELETE CASCADE), база не знает, где искать связанные строки вorder_items. Ей опять придётся сканировать всю дочернюю таблицу целиком, чтобы найти все позиции для удаления. Это просто терпения ноль ебать. - Ну и любые запросы с
JOINпо этому полю будут тормозить как мартышлюшка в январской луже. Оптимизатор просто разведёт руками и пойдёт делать тот самый полный сканирующий ёб... прости, поиск.
Как это делается (на примере MySQL/PostgreSQL):
-- Часто индекс создаётся автоматом, когда ты FOREIGN KEY объявляешь.
-- Но надеяться на авось — это путь вникуда. Лучше явно прописать, чтобы спать спокойно.
ALTER TABLE `order_items`
ADD INDEX `idx_order_id` (`order_id`), -- Вот он, наш спаситель!
ADD CONSTRAINT `fk_order_items_orders`
FOREIGN KEY (`order_id`) REFERENCES `orders`(`id`)
ON DELETE CASCADE;
Исключения? Да они почти мифические!
- Ну, допустим, у тебя табличка-справочник размером в пять строк, типа
statuses. На неё все ссылаются, но она микроскопическая. В этом случае, может, и не нужен индекс — сканировать пять строк не страшно. - Или вот ещё фантастический сценарий: есть таблица, в которую только пишут (какой-нибудь лог), и ты никогда-никогда не делаешь по этому ключу
SELECT,JOINилиDELETEиз родительской таблицы. Ну такое представить сложно, честно говоря.
Вывод простой, как три копейки: видишь внешний ключ — тут же лепи на него индекс. Не усложняй жизнь ни себе, ни базе данных. Иначе потом будешь сам от себя охуевать, глядя на графики медленных запросов.