Присоединение таблиц в запросах — это базовый инструмент в работе с базами данных. Давайте рассмотрим какие присоединения (JOIN) бывают, и что от этого меняется в результатах запроса.
Для начала создадим две таблицы, над которыми будем проводить опыты. Это таблица с именами сотрудников и словарь с перечнем должностей.
Persons (Сотрудники)
Столбец position_ref (от «position reference») это ссылка на следующую таблицу, где перечислены должности сотрудников.
Positions (должности)
Т.е. чтобы узнать должность сотрудника в таблице Persons, нужно присоединить соответствующие данные из таблицы Positions, связывая их по значениям position_ref и id_pos.
Далее мы рассмотрим все варианты присоединений. Данные специально подобраны так, чтобы продемонстрировать отличия в результатах разных запросов.
INNER JOIN
Внутреннее присоединение. Равносильно просто JOIN или CROSS JOIN (верно для MYSQL, в стандарте SQL INNER JOIN не эквивалентен синтаксически CROSS JOIN, т.к. используется с выражением ON).
1 2 3 4 5 |
SELECT id_person, name, id_pos, title FROM `persons` INNER JOIN `positions` ON id_pos = position_ref |
Такое присоединение покажет нам данные из таблиц, только если условие связывания соблюдается — т.е. для сотрудника указан существующий в словаре идентификатор должности.
Если поменять порядок соединения таблиц — получим тот же результат.
Условно представим себе эти таблицы, как пересекающиеся множества, где пересечение — это наличие связи между таблицами. Получим картинку:
Далее проследим как получить разные части (подмножества) данного множества.
OUTER JOIN
Внешнее присоединение. Различают LEFT OUTER JOIN и RIGHT OUTER JOIN, и обычно опускают слово «OUTER».
Внешнее присоединение включает в себя результаты запроса INNER и добавляются «неиспользованные» строки из одной из таблиц. Какую таблицу использовать в качестве «добавки» — указывает токен LEFT или RIGHT.
LEFT JOIN
Внешнее присоединение «слева».
1 2 3 4 5 |
SELECT id_person, name, id_pos, title FROM `persons` LEFT OUTER JOIN `positions` ON id_pos = position_ref |
«Левая» таблица persons, содержит строку id_person#3 — «Александр», где указан идентификатор должности, отсутствующей в словаре. Мы увидим все записи из «левой» таблицы, тогда как правая будет присоединена по возможности.
На картинке это можно показать вот так:
RIGHT JOIN
Присоединение «справа».
1 2 3 4 5 |
SELECT id_person, name, id_pos, title FROM persons RIGHT OUTER JOIN positions ON id_pos = position_ref |
Тут данные из «левой» таблицы присоединяются к «правой».
Словарь должностей (правая таблица) содержит неиспользуемую запись с id_pos#3 — «программист». Теперь она попала в результат запроса.
Полное множество
MySQL не знает соединения FULL OUTER JOIN. Что если нужно получить полное множество?
Первый способ — объединение запросов LEFT и RIGHT.
1 2 3 4 5 6 7 8 9 10 11 |
(SELECT id_person, name, id_pos, title FROM persons LEFT OUTER JOIN positions ON id_pos = position_ref) UNION (SELECT id_person, name, id_pos, title FROM persons RIGHT OUTER JOIN positions ON id_pos = position_ref) |
При таком вызове UNION, после слияния результатов, SQL отсечет дубли (как DISTINCT). Для отсечения дублей SQL прибегает к сортировке. Это может сказываться на быстродействии.
Второй способ — объединение LEFT и RIGHT, но в одном из запросов мы исключаем часть, соответствующую INNER. А объединение задаём как UNION ALL, что позволяет движку SQL обойтись без сортировки.
1 2 3 4 5 6 7 8 9 10 11 |
(SELECT id_person, name, id_pos, title FROM persons LEFT OUTER JOIN positions ON id_pos = position_ref) UNION ALL (SELECT id_person, name, id_pos, title FROM persons RIGHT OUTER JOIN positions ON id_pos = position_ref WHERE id_person is NULL) |
Этот пример показывает нам как исключить пересечение и получить только левую или правую часть множества.
Левое подмножество
LEFT JOIN ограничиваем проверкой, что данных из второй таблицы нет.
1 2 3 4 5 6 |
SELECT id_person, name, id_pos, title FROM persons LEFT OUTER JOIN positions ON id_pos = position_ref WHERE id_pos is NULL |
В нашем примере — это специалисты, у которых не задана должность или нет должности с указанным ключом.
Правое подмножество
Аналогично выделяем правую часть.
1 2 3 4 5 6 |
SELECT id_person, name, id_pos, title FROM persons RIGHT OUTER JOIN positions ON id_pos = position_ref WHERE id_person is NULL |
В нашем случае получим должности, которые никому не назначены.
Всё кроме пересечения
Остался один вариант, тот когда исключено пересечение множеств. Его можно сложить из двух предыдущих запросов через UNION ALL (т.к. подмножества не пересекаются).
1 2 3 4 5 6 7 8 9 10 11 12 13 |
(SELECT id_person, name, id_pos, title FROM persons LEFT OUTER JOIN positions ON id_pos = position_ref WHERE id_pos is NULL) UNION ALL (SELECT id_person, name, id_pos, title FROM persons RIGHT OUTER JOIN positions ON id_pos = position_ref WHERE id_person is NULL) |
Это запрос соберет все случаи, когда по какой то причине данные из таблиц не связаны.
А графически такое объединение выглядит следующим образом:
Спасибо автору! мне очень помогло я егор
может всетаки тут 6 должна быть ? http://prntscr.com/ky49nk
Нет, Александр, это поле из таблицы Positions, где нет записи с id == 6.
Чувак ты меня спас!
как-то страноо. LEFT OUTER JOIN показывает левое множество и оно же показывает левое без пересечения.
Запросы отличаются наличием условия WHERE ps.id is NULL.
Подскажите пожалуйста, можно ли соединить 3 таблицы чтобы вывести 1 столбец из третей таблицы. к примеру :
Таблица City :
id , namecity.
Таблица events :
id, year.
Таблица posts :
id, title, date, description, text.
Можно ли объединив эти таблицы вывести столбец text из posts?
Соединять можно любые таблицы, но судя по структуре, ваши таблицы пока никак не связаны между собой.
Спасибо автору! мне очень помогло я миша
Спасибо! С множествами в виде графики всё стало понятно.
Автор, добра тебе! Очень понятно и наглядно все разъяснил:)
Большое спасибо за понятное и наглядное объяснение.
Спасибо. Очень наглядно и доходчиво.