Часть 6 из 16
У нас уже есть события, площадки и запросы, которые выводят вычисленные поля. Но афиша должна ответить и на главный вопрос: кто где выступает? Артист может участвовать в разных событиях, а в одном событии бывает несколько слотов, поэтому простого поля artist в таблице events недостаточно.
Создадим таблицу выступлений-связку и соберём первое цельное расписание через JOIN. По ходу разберём, почему в результате повторяются названия событий и когда вместо обычного JOIN нужен LEFT JOIN.
Таблицы артистов и выступлений
CREATE TABLE artists (
artist_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
stage_name VARCHAR(120) NOT NULL,
genre VARCHAR(60) NOT NULL,
PRIMARY KEY (artist_id), UNIQUE KEY uq_artist_name(stage_name)
) ENGINE=InnoDB;
CREATE TABLE performances (
performance_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
event_id BIGINT UNSIGNED NOT NULL,
artist_id BIGINT UNSIGNED NOT NULL,
stage_label VARCHAR(80) NOT NULL,
starts_at DATETIME NOT NULL,
ends_at DATETIME NOT NULL,
PRIMARY KEY (performance_id),
CHECK (ends_at > starts_at),
FOREIGN KEY (event_id) REFERENCES events(event_id),
FOREIGN KEY (artist_id) REFERENCES artists(artist_id)
) ENGINE=InnoDB;
Каждая строка performances — один слот. Для тренировки добавьте артистов через INSERT и используйте реальные ID событий и артистов.
Собираем расписание JOIN
SELECT e.name AS event_name, v.name AS venue,
p.starts_at, p.stage_label, a.stage_name AS artist
FROM performances AS p
JOIN events AS e ON e.event_id=p.event_id
JOIN venues AS v ON v.venue_id=e.venue_id
JOIN artists AS a ON a.artist_id=p.artist_id
ORDER BY p.starts_at;
ON указывает, по каким ключам связываются строки. Псевдонимы сокращают запись и помогают различить одноимённые столбцы.
Когда выбрать LEFT JOIN
INNER JOIN оставляет только строки с парой. Если нужно показать и события без назначенных артистов, начните с events и используйте LEFT JOIN performances. Поля отсутствующего слота станут NULL. Далее добавим продажи и агрегаты.
Как JOIN собирает одну строку результата
Представьте строки с ID: событие 1 ссылается на площадку 1, а слот 1 ссылается на событие 1 и артиста 2. Условие e.event_id = p.event_id заставляет MySQL сопоставить эти записи. Затем второе соединение находит название площадки, третье — сценическое имя артиста.
JOIN не склеивает таблицы навсегда: он формирует результат только на время запроса.
В запросе есть два столбца с названием name, поэтому мы используем алиасы event_name и venue. Префикс таблицы в e.name устраняет неоднозначность. Алиасы таблиц e, p, v и a — короткие имена, действующие в запросе.
Почему иногда строка исчезает
Обычный JOIN показывает только строки с совпадением по ключу. Если событие ещё без артистов, оно не появится в запросе, который начинается с performances. Для списка всех событий начните с events и сделайте LEFT JOIN performances: событие останется, а колонки слота будут NULL.
Практика: удалите из тестовой базы одного слота только после сохранения копии и сравните INNER и LEFT JOIN. Затем восстановите слот. В следующей части будем суммировать данные и проверим, как JOIN может случайно увеличить выручку.
Учимся диагностировать JOIN
Если имя артиста показывается у чужого события, проверьте условие ON: оба ключа должны сравниваться с соответствующей таблицей. Если запрос вернул слишком много строк, проверьте кардинальность связи: одно событие закономерно имеет несколько слотов, значит название события повторится — по одной строке на каждый слот.
Повтор здесь не ошибка, если запрос предназначен показать расписание.
Добавляйте таблицы по одной. Сначала выполните JOIN событий и площадок; проверьте ID и названия. Потом добавьте performances, затем artists. Так легче определить, на каком шаге появляются неожиданные строки.
JOIN и NULL в условиях
При LEFT JOIN отсутствующая сторона получает NULL. Если сразу написать WHERE p.starts_at > ..., строки без выступлений отсеются, потому что сравнение с NULL не истинно; результат фактически станет похож на INNER JOIN.
Если нужно сохранить пустые события и отфильтровать только совпавшие выступления, иногда условие надо перенести в ON. Это тонкое, но важное различие при построении полного расписания.
Для проверки временно выведите ключи обеих таблиц: e.event_id и p.performance_id. Видя их рядом, проще понять, почему конкретная строка появилась.
JOIN или LEFT JOIN?
| Вид соединения | Что попадёт в результат | Пример задачи |
|---|---|---|
| JOIN | Только строки с найденной парой | Показать слоты с артистами |
| LEFT JOIN | Все строки левой таблицы, даже без пары | Показать события без артистов |
Перед соединением решите, нужно ли сохранить записи без пары. Это требование определяет тип JOIN и помогает понять, почему часть событий исчезла из афиши.
← Предыдущая статья: Вычисления и даты в SQL: готовим расписание фестиваля
Следующая статья: GROUP BY и SUM в MySQL: считаем продажи билетов →
0
23