Часто мы используем несколько таблиц в базе данных, чтобы соответствовать лучшим практикам нормализации. Среди обычных форм мы также должны обеспечить некоторую целостность связанных данных, распределенных между несколькими таблицами. Для этого можно использовать FOREIGN KEY вместе с определенными ограничениями…

Примечание: все данные, имена или наименования, найденные в базе данных, представленной в этом посте, строго используются для практики, обучения, обучения и тестирования. Он ни в коем случае не отображает фактические данные, принадлежащие или используемые какой-либо стороной или организацией.
Используемая ОС и БД:

  • Xubuntu Linux 16.04.5 LTS (Xenial Xerus)
  • MySQL 5.7.23

В этом сообщении блога мы рассмотрим использование FOREIGN KEY для обеспечения надежных методов работы с данными и ссылочной целостности между таблицами, содержащими связанные данные. Я смоделирую эти отношения, используя фиктивную «книгу» и сопроводительную таблицу «жанров».

Давайте посмотрим на подробное описание и состав таблиц для целевых таблиц, используя SHOW CREATE TABLE синтаксические запросы:

mysql> SHOW CREATE TABLE book\G
 *************************** 1. row ***************************
 Table: book
 Create Table: CREATE TABLE `book` (
 `bk_id` int(11) NOT NULL AUTO_INCREMENT,
 `title` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
 `genre_id` int(11) NOT NULL,
 PRIMARY KEY (`bk_id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
 1 row in set (0.00 sec)
mysql> SHOW CREATE TABLE book_genre\G
 *************************** 1. row ***************************
 Table: book_genre
 Create Table: CREATE TABLE `book_genre` (
 `genre_id` int(11) NOT NULL,
 `genre_type` varchar(35) COLLATE utf8mb4_unicode_ci DEFAULT NULL
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
 1 row in set (0.00 sec)

(Вы знакомы с командой SHOW? Взгляните на Примеры использования синтаксиса MySQL SHOW. Я писал об использовании этой удобной команды.)

Я запущу эти два исследовательских запроса, чтобы получить представление о текущих данных:

mysql> SELECT * FROM book;
 + — — — -+ — — — — — — — — — + — — — — — +
 | bk_id  | title             | genre_id  |
 + — — — -+ — — — — — — — — — + — — — — — +
 | 1      | No Upper Limit    | 202       |
 | 2      | The Lost Helm     | 101       |
 | 3      | Golden Band       | 303       |
 | 4      | 10th Grade Blues  | 404       |
 | 5      | God Captain       | 101       |
 + — — — -+ — — — — — — — — — + — — — — — +
 5 rows in set (0.00 sec)
mysql> SELECT * FROM book_genre;
 + — — — — — + — — — — — — -+
 | genre_id  | genre_type   |
 + — — — — — + — — — — — — -+
 | 101       | Fantasy      |
 | 202       | Action       |
 | 303       | Romance      |
 | 404       | Young Adult  |
 + — — — — — + — — — — — — -+
 4 rows in set (0.00 sec)

Предположим, мы сделали это INSERT на таблице book:

mysql> INSERT INTO book(title, genre_id)
    -> VALUES (‘Blunt Spurs’, 505);
 Query OK, 1 row affected (0.02 sec)

Хотя ошибок не произошло и снаружи все выглядит нормально, существует основная проблема. Таблица book_genre не имеет genre_id значения 505. Это не идеально и создает проблемы в поддержании согласованности данных и целостности нескольких таблиц. Один из способов предотвратить возникновение такого рода несоответствий в наших данных - использовать FOREIGN KEY.

Добавить или включить FOREIGN KEY в столбец таблицы в MySQL относительно просто. Однако есть несколько вещей, о которых нужно помнить, и вы увидите, когда я буду двигаться дальше. Прежде всего следует отметить, что FOREIGN KEY должен находиться в дочерней таблице. В данном примере это таблица book.

Поскольку таблица book уже существует, я воспользуюсь командой ALTER TABLE, чтобы добавить FOREIGN KEY:

mysql> ALTER TABLE book ADD CONSTRAINT FOREIGN KEY fk_bk_genre (genre_id) REFERENCES book_genre(genre_id) ON UPDATE CASCADE ON DELETE NO ACTION;
ERROR 1215 (HY000): Cannot add foreign key constraint

Поиск дополнительной и полезной информации в сообществе

По общему признанию, я не понял сообщение об ошибке при запуске этой команды и обратился к документации за подсказками / смыслом. Я пришел к выводу, что причина в том, что указанный столбец в родительской таблице не является уникальным. По мере продвижения сообщения в блоге я исправляю это, делая столбец genre_id (ссылка) в таблице genre (родительская таблица) PRIMARY KEY. Что делает этот столбец уникальным. Кроме того, в документации (ссылка на которую приведена ниже) представляет интерес этот отрывок:

Взаимосвязи внешнего ключа включают родительскую таблицу, в которой хранятся центральные значения данных, и дочернюю таблицу с идентичными значениями, указывающими на ее родительскую.

Кроме того, в документации упоминается, что при добавлении FOREIGN KEY к существующей таблице с ALTER TABLE обязательно сначала создайте необходимые индексы.

Мы очень благодарны за любые исправления, информацию, идеи или дополнительные ресурсы, предоставленные читателями и сообществом.
Поддерживающие источники:

Как я упоминал выше, в этом операторе MySQL я делаю genre_id PRIMARY KEY для таблицы book_genre:

mysql> ALTER TABLE book_genre ADD CONSTRAINT PRIMARY KEY pk_genre_id (genre_id);
Query OK, 0 rows affected (0.65 sec)
Records: 0 Duplicates: 0 Warnings: 0

Давайте еще раз быстро просмотрим описание этой таблицы и увидим, что PRIMARY key на месте:

mysql> SHOW CREATE TABLE book_genre\G
 *************************** 1. row ***************************
 Table: book_genre
 Create Table: CREATE TABLE `book_genre` (
 `genre_id` int(11) NOT NULL,
 `genre_type` varchar(35) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 PRIMARY KEY (`genre_id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
 1 row in set (0.02 sec)

Теперь, чтобы добавить FOREIGN KEY в таблицу book:

mysql> ALTER TABLE book ADD CONSTRAINT FOREIGN KEY fk_bk_genre (genre_id) REFERENCES book_genre(genre_id) ON UPDATE CASCADE ON DELETE NO ACTION;
 ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`practice`.`#sql-613_a0`, CONSTRAINT `#sql-613_a0_ibfk_1` FOREIGN KEY (`genre_id`) REFERENCES `book_genre` (`genre_id`) ON DELETE NO ACTION ON UPDATE CASCADE)

Прежде чем объяснять, что означает указанная выше ошибка, давайте вернемся к данным в таблице book, поскольку в этом заключается проблема:

mysql> SELECT * FROM book;
 + — — — -+ — — — — — — — — — + — — — — — +
 | bk_id  | title             | genre_id  |
 + — — — -+ — — — — — — — — — + — — — — — +
 | 1      | No Upper Limit    | 202       |
 | 2      | The Lost Helm     | 101       |
 | 3      | Golden Band       | 303       |
 | 4      | 10th Grade Blues  | 404       |
 | 5      | God Captain       | 101       |
 | 6      | Blunt Spurs       | 505       |
 + — — — -+ — — — — — — — — — + — — — — — +
 6 rows in set (0.00 sec)

Напомним, что genre_id значение столбца 505 в таблице book_genre отсутствует. И вот, проблема обнаружена. (См. Процитированный отрывок из вышеупомянутой документации относительно идентичных данных между родительской и дочерней таблицами).

Чтобы исправить ошибку, я INSERT это значение 505 с соответствующим genre_type:

mysql> INSERT INTO book_genre(genre_id, genre_type)
    -> VALUES (505, ‘Western’);
Query OK, 1 row affected (0.04 sec)

Наконец, мы можем создать FOREIGN KEY в таблице book:

mysql> ALTER TABLE book ADD CONSTRAINT FOREIGN KEY fk_bk_genre (genre_id) REFERENCES book_genre(genre_id) ON UPDATE CASCADE ON DELETE NO ACTION;
 Query OK, 6 rows affected (0.68 sec)
 Records: 6 Duplicates: 0 Warnings: 0

Установив взаимосвязь между PRIMARY KEY таблицы book_genre и FOREIGN KEY в таблице book, теперь у нас есть средства для обеспечения ссылочной целостности вместе со сплошным столбцом, который можно использовать для связывания совпадающих строк с помощью JOIN.

Вот пример запроса, возвращающего эти книги в жанре 'Fantasy':

mysql> SELECT b.title, bk.genre_type
    -> FROM book AS b
    -> INNER JOIN 
    -> book_genre AS bk
    -> USING(genre_id)
    -> WHERE bk.genre_type = ‘Fantasy’;
 + — — — — — — — -+ — — — — — — +
 | title          | genre_type  |
 + — — — — — — — -+ — — — — — — +
 | The Lost Helm  | Fantasy     |
 | God Captain    | Fantasy     |
 + — — — — — — — -+ — — — — — — +
 2 rows in set (0.04 sec)

В дополнение к столбцу-кандидату для присоединения отношение FOREIGN KEY запрещает те INSERT ', у которых нет совпадающего значения в родительской таблице. Как в этом примере:

mysql> INSERT INTO book(title, genre_id)
    -> VALUES(‘Broken Scope’, 797);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`practice`.`book`, CONSTRAINT `book_ibfk_1` FOREIGN KEY (`genre_id`) REFERENCES `book_genre` (`genre_id`) ON DELETE NO ACTION ON UPDATE CASCADE)

Вышеупомянутый INSERT завершился неудачно, поскольку предоставленный genre_id из 797 не существует в таблице book_genre.

Следует отметить, что FOREIGN KEY также может быть установлен в начальной команде CREATE TABLE.

Дополнительная литература и ресурсы

Я надеюсь, что это сообщение в блоге предоставило вам достаточно информации и, что более важно, любопытство, чтобы узнать больше о FOREIGN KEY. Однако мы еще не закончили. В следующем сообщении блога я расскажу о ссылочных действиях (например, ON UPDATE CASCADE), указанных в примерах, приведенных в этом сообщении блога. Обязательно посетите этот пост, когда он будет опубликован. Я с нетерпением жду любых комментариев, которые у вас есть ниже, и большое спасибо за чтение !!!
Изучите официальное MySQL 5.7 Online Manual для получения дополнительной информации.

Призыв к действию!

Спасибо, что нашли время прочитать этот пост. Я искренне надеюсь, что вы открыли для себя что-то интересное и поучительное. Пожалуйста, поделитесь своими выводами здесь с кем-нибудь, кого вы знаете, кто тоже получит от этого такую ​​же ценность.

Посетите страницу Портфолио-проекты, чтобы увидеть сообщения в блоге / технические статьи, которые я написал для клиентов.

Я уже упоминал, как я люблю чашку кофе?!?!

Чтобы получать уведомления по электронной почте (Никогда не спамить) от этого блога (Проза Цифровой Совы) о последних публикациях в блоге по мере их публикации, пожалуйста, подпишитесь (по собственному желанию), нажав кнопку Нажмите, чтобы подписаться! На странице боковая панель на главной странице! (Не стесняйтесь в любое время просмотреть страницу политики конфиденциальности Digital Owl's Prose по любым вопросам, которые могут у вас возникнуть: обновления по электронной почте, подписка, отказ, контактные формы и т. Д.)

Обязательно посетите страницу Best Of, где собраны мои лучшие сообщения в блоге.

Джош Отвелл хочет учиться и расти как разработчик SQL и блогер. Другие любимые занятия находят его, уткнувшись носом в хорошую книгу, статью или командную строку Linux. Среди них он разделяет любовь к настольным ролевым играм, чтению фэнтезийных романов и проведению времени с женой и двумя дочерьми.

Отказ от ответственности: примеры, представленные в этом посте, представляют собой гипотетические идеи о том, как достичь аналогичных результатов. Это не самое лучшее решение. Большинство, если не все, из представленных примеров выполняются в среде рабочей станции для личного развития / обучения и не должны считаться производственными или готовыми к использованию. Ваши конкретные цели и потребности могут отличаться. Используйте те методы, которые лучше всего подходят для ваших нужд и целей. Мнения мои собственные.

Первоначально опубликовано на joshuaotwell.com 12 сентября 2018 г.

✉️ Подпишитесь на рассылку еженедельно Email Blast от CodeBurst 🐦 Подпишитесь на CodeBurst на Twitter , просмотрите 🗺️ Дорожная карта веб-разработчиков на 2018 год и 🕸️ Изучите веб-разработку с полным стеком .