Визуальное представление соединений JOIN в SQL
'JOIN
' – ключевое слово в SQL, используемое для запроса данных из двух и более связанных таблиц. Данная статья – это попытка кратко и лаконично объяснить работу с JOIN
себе и всем, кто заинтересован в этом.
Связанные таблицы
Хорошо спроектированная реляционная база данных включает в себя ряд таблиц, содержащих связанные данные. В качестве очень простого примера данных используем пользователей (студентов) и зачисления на курсы:
id | name | course |
---|---|---|
1 | Alice | 1 |
2 | Bob | 1 |
3 | Caroline | 2 |
4 | David | 5 |
5 | Emma | (NULL) |
Код MySQL для создания таблицы:
CREATE TABLE `user` (
`id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(30) NOT NULL,
`course` smallint(5) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
Номера курсов связаны с объектами, находящимися в таблице курсов...
id | name |
---|---|
1 | HTML5 |
2 | CSS3 |
3 | JavaScript |
4 | PHP |
5 | MySQL |
Код MySQL для создания таблицы:
CREATE TABLE `course` (
`id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
Поскольку мы используем таблицы InnoDB и знаем, что user.course и course.id связаны, то можем задать связь внешним ключом:
ALTER TABLE `user`
ADD CONSTRAINT `FK_course`
FOREIGN KEY (`course`) REFERENCES `course` (`id`)
ON UPDATE CASCADE;
В сущности, MySQL будет автоматически:
- перенумеровывать связанные записи в столбце
user.course
при изменениях вcourse.id
; - отклонять любые попытки удалить курс, на который зачислены пользователи.
Важно: эта база данных спроектирована ужасно!
Эта база данных неэффективна. Она нормальна для примера, но студент в ней может быть записан либо никуда, либо только на один курс. В реальной системе такого ограничения быть не должно, для этого можно использовать промежуточную таблицу «регистрация», которая будет связывать любое количество студентов с любым количеством курсов.
INNER JOIN
(или просто JOIN
)
INNER JOIN
является наиболее часто используемым. Он дает набор записей, которые совпадают в обоих таблицах, и в user
, и в course
, то есть все пользователи, зачисленные на курсы:
SELECT user.name, course.name
FROM `user`
INNER JOIN `course` on user.course = course.id;
Результат:
user.name | course.name |
---|---|
Alice | HTML5 |
Bob | HTML5 |
Caroline | CSS3 |
David | MySQL |
LEFT JOIN
А что делать, если нам необходим список всех студентов и их курсов, даже если они не зачислены ни на один курс? LEFT JOIN
даст набор записей, которые соответствуют каждой записи в левой таблице (user
) независимо от соответствия записи в правой таблице (course
):
SELECT user.name, course.name
FROM `user`
LEFT JOIN `course` on user.course = course.id;
user.name | course.name |
---|---|
Alice | HTML5 |
Bob | HTML5 |
Carline | CSS3 |
David | MySQL |
Emma | (NULL) |
Добавив условие
WHERE course.id IS NULL
мы получим список студентов, не зачисленных ни на один курс. И диаграмма для такого запроса будет следующей:
RIGHT JOIN
Возможно, нам потребуется список всех курсов и студентов, даже если никто из них не был зачислен? RIGHT JOIN
дает набор записей, которые соответствуют каждой записи в правой таблице (course
) независимо от соответствия записи в левой таблице (user
):
SELECT user.name, course.name
FROM `user`
RIGHT JOIN `course` on user.course = course.id;
Результат:
user.name | course.name |
---|---|
Alice | HTML5 |
Bob | HTML5 |
Carline | CSS3 |
(NULL) | JavaScript |
(NULL) | PHP |
David | MySQL |
RIGHT JOIN
используется редко, так как вы можете получить тот же результат, используя LEFT JOIN
. Следующий код может быть более эффективным и быстрым для парсинга СУБД:
SELECT user.name, course.name
FROM `course`
LEFT JOIN `user` on user.course = course.id;
Мы могли бы, например, подсчитать количество студентов, обучающихся на каждом курсе:
SELECT course.name, COUNT(user.name)
FROM `course`
LEFT JOIN `user` ON user.course = course.id
GROUP BY course.id;
Результат:
course.name | count() |
---|---|
HTML5 | 2 |
CSS3 | 1 |
JavaScript | 0 |
PHP | 0 |
MySQL | 1 |
OUTER JOIN
(или FULL OUTER JOIN
)
OUTER JOIN
возвращает все записи из обеих таблиц независимо от совпадений. Там где совпадения нет, на недостающей стороне будет содержаться NULL
.
OUTER JOIN
менее полезен по сравнению с INNER
, LEFT
или RIGHT
, и он не реализован в MySQL. Тем не менее, вы можете обойти это ограничение, используя UNION
для LEFT JOIN
и RIGHT JOIN
, например:
SELECT user.name, course.name
FROM `user`
LEFT JOIN `course` on user.course = course.id
UNION
SELECT user.name, course.name
FROM `user`
RIGHT JOIN `course` on user.course = course.id;
Результат:
user.name | course.name |
---|---|
Alice | HTML5 |
Bob | HTML5 |
Carline | CSS3 |
David | MySQL |
Emma | (NULL) |
(NULL) | JavaScript |
(NULL) | PHP |
Вот и всё! Надеюсь этот материал будет кому-то полезен.