Часть 2 из 16

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

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

Создаём учебную базу

CREATE DATABASE city_festival CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
USE city_festival;

utf8mb4 поддерживает Unicode целиком. Если конкретная колляция недоступна на вашей версии, найдите подходящую через SHOW COLLATION.

Таблица площадок

CREATE TABLE venues (
 venue_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
 name VARCHAR(120) NOT NULL,
 district VARCHAR(80) NOT NULL,
 capacity INT UNSIGNED NOT NULL,
 PRIMARY KEY (venue_id),
 CHECK (capacity > 0)
) ENGINE=InnoDB;

Первичный ключ не зависит от названия. NOT NULL требует значение, а CHECK запрещает вместимость ноль или меньше.

События ссылаются на площадку

CREATE TABLE events (
 event_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
 venue_id BIGINT UNSIGNED NOT NULL,
 name VARCHAR(160) NOT NULL,
 starts_at DATETIME NOT NULL,
 ends_at DATETIME NOT NULL,
 status VARCHAR(20) NOT NULL DEFAULT 'planned',
 PRIMARY KEY (event_id),
 CHECK (ends_at > starts_at),
 FOREIGN KEY (venue_id) REFERENCES venues(venue_id) ON DELETE RESTRICT
) ENGINE=InnoDB;

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

Осматриваем структуру

Выполните SHOW TABLES и SHOW CREATE TABLE events, затем сравните результат с вкладкой «Структура» phpMyAdmin. В следующей части добавим строки и научимся читать их запросом.

Как читать объявление таблицы

Команда CREATE TABLE — это договор с базой о форме каждой будущей строки. Названия и типы полей определяют, что можно сохранить; ключи определяют идентичность и связи; ограничения отклоняют недопустимые состояния. Например, если событие заканчивается раньше начала, ограничение времени вернёт ошибку прямо во время INSERT.

Важно: ограничение помогает лишь тогда, когда это правило действительно универсально для всех строк.

Тип выбирайте по смыслу. VARCHAR подходит для названий переменной длины; целое число — для вместимости; DATETIME — для даты и времени; для цены позднее возьмём точный DECIMAL.

Не храните дату как текст: сортировка строк может дать не хронологический порядок, а сервер не сможет проверять дату как дату.

Если MySQL выдаёт ошибку при создании внешнего ключа, сначала сверьте типы обоих venue_id: они должны совпадать, включая UNSIGNED. Затем проверьте, что обе таблицы используют InnoDB. В phpMyAdmin загляните в «Структуру» и найдите блоки «Индексы» и «Связи».

В следующем уроке мы наполним схему и проверим эти правила практикой.

Первичный ключ и внешний ключ — разные задачи

Первичный ключ отвечает на вопрос «какая это строка?». Значение venue_id уникально для каждой площадки и не должно быть пустым. Внешний ключ отвечает на вопрос «с какой строкой она связана?»: поле events.venue_id указывает на ключ площадки.

Название площадки для связи не подходит: оно может измениться или случайно повториться.

Проверка схемы на практике: после создания таблиц попробуйте добавить событие с venue_id, которого нет в venues. MySQL откажет из-за внешнего ключа. Затем добавьте площадку и повторите INSERT с её настоящим ID.

Так становится понятно, почему структура — не просто набор колонок, а набор правил допустимых данных.

Если это была не отдельная пустая база, не создавайте таблицу поверх существующей. Команда CREATE TABLE должна выполниться один раз; перед экспериментом используйте отдельную схему или удаляйте только учебную таблицу, чьи данные точно не нужны.

Шпаргалка по типам в нашей схеме

Тип Для чего Пример
BIGINT UNSIGNED Идентификатор без отрицательных значений venue_id
VARCHAR(n) Текст ограниченной длины name
INT UNSIGNED Целое неотрицательное число capacity
DATETIME Дата и время без зоны starts_at

Выбирайте тип по смыслу поля. Тогда MySQL сможет проверять значения и выполнять подходящие операции.

← Предыдущая статья: SQL на практике: создаём базу MySQL в phpMyAdmin

Следующая статья: INSERT и SELECT в MySQL: добавляем первые данные →