Типы JOIN. Основы, комбинации, практика
Привет, Хабр! Меня зовут Татьяна Ошуркова, я разработчик и системный аналитик. В этой статье я затрону базовую теорию по SQL — оператор JOIN. На примерах мы разберем, как использовать каждый из видов соединений в отдельности, а также посмотрим, как можно комбинировать несколько типов соединений в одном запросе и какой результат мы можем получить.
На мой взгляд, теория по соединениям таблиц и принципам их работы одна из самых интересных. Так как соединения — мощный инструмент, который может решить множество задач.
Подписывайтесь на канал в Telegram, где можно найти больше полезных материалов и вебинаров.
Недавно стартовала запись в мою Школу системного анализа, где за 10 недель вы освоите все необходимые навыки на практике с реальными кейсами и получите карьерную консультацию. А до 1 декабря можно получить бонусный курс «Разработка чат-бота на Python».
Начнем с основных понятий. JOIN — это оператор, который используется для объединения строк из двух или более таблиц на основе определенных условий, обычно через общие столбцы (ключи). В реляционной алгебре (математической основе реляционных баз данных) операция соединения — это операция, которая объединяет кортежи (строки) из двух отношений (таблиц), если они удовлетворяют определённому условию. SQL реализует эту математическую операцию через оператор JOIN. Это связывает SQL с теоретическими основами реляционных баз данных.
INNER JOIN (или просто JOIN)
Один из самых распространенных типов соединений, который используется для объединения строк таблиц на основе определенного условия. Это соединение возвращает только те строки, которые имеют совпадения в обеих таблицах по указанным ключевым полям.
Данный тип соединения аналогичен перечислению таблиц в секции FROM с дальнейшим условием соединения в секции WHERE. Преимуществом использования JOIN является явное указания условия соединения таблицы, что помогает избежать возможных ошибок при использовании FROM и WHERE.
SELECT e.first_name, e.last_name, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id
У нас есть две таблицы: сотрудники и их департаменты. В таблице сотрудники (employees) есть поле department_id (идентификатор департамента). Запрос вернет список сотрудников с наименованиями департаментов. В результате мы получим только тех сотрудников, у кого был указан департамент — не пустое поле department_id.
LEFT OUTER JOIN (или LEFT JOIN)
Тип соединения, который позволяет объединять строки таблиц на основе условия соединения, возвращая все строки из левой таблицы и только соответствующие строки из правой таблицы. Если в правой таблице нет соответствующей строки, в результирующем наборе для этих строк будут возвращены NULL-значения. Рассмотрим предыдущий пример, только теперь используем LEFT JOIN.
SELECT e.first_name, e.last_name, d.department_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id
Если в первом случае в результирующую выборку попали только сотрудники, у которых был указан department_id, то теперь мы получим список абсолютно всех сотрудников, но у тех, у кого был не указан department_id, в результате будет не заполнено наименование департамента.
Используемые таблицы
Таблица employees:
employee_id | first_name | last_name | department_id |
1 | John | Doe | 10 |
2 | Jane | Smith | 20 |
3 | Emily | Davis | NULL |
Таблица departments:
department_id | department_name |
10 | HR |
20 | IT |
Результат:
first_name | last_name | department_name |
John | Doe | HR |
Jane | Smith | IT |
Emily | Davis | NULL |
RIGHT OUTER JOIN (или RIGHT JOIN)
Тип соединения, который позволяет объединять строки таблиц на основе условия соединения, возвращая все строки из правой таблицы и только соответствующие строки из левой таблицы. Если в левой таблице нет соответствующей строки, в результирующем наборе для этих строк будут возвращены NULL-значения.
Если выполнить предыдущий запрос, заменив LEFT JOIN на RIGHT JOIN, в результирующей выборке мы получим сотрудников, у кого был заполнен department_id, а также департаменты, где отсутствуют сотрудники — ни у одного сотрудника нет соответствующего department_id.
SELECT e.first_name, e.last_name, d.department_name
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.department_id
Предположим, что добавился дополнительный департамент «Marketing».
Используемые таблицы
Таблица departments:
department_id | department_name |
10 | HR |
20 | IT |
30 | Marketing |
Результат:
first_name | last_name | department_name |
John | Doe | HR |
Jane | Smith | IT |
NULL | NULL | Marketing |
FULL OUTER JOIN
Тип соединения, который позволяет объединять строки из двух таблиц на основе условия соединения, возвращая все строки из обеих таблиц. Если для строки из одной таблицы нет соответствующей строки в другой таблице, в результирующем наборе будут возвращены NULL-значения для столбцов отсутствующей таблицы.
SELECT e.first_name, e.last_name, d.department_name
FROM employees e
FULL OUTER JOIN departments d ON e.department_id = d.department_id
Можно сказать, что результат данного соединения является объединением результата использования LEFT и RIGHT JOIN.
Если взять таблицы, используемые в предыдущем запросе, то получим следующий результат:
first_name | last_name | department_name |
John | Doe | HR |
Jane | Smith | IT |
NULL | NULL | Marketing |
Emily | Davis | NULL |
CROSS JOIN
Тип соединения, который возвращает декартово произведение таблиц. Это означает, что каждая строка из первой таблицы соединяется с каждой строкой из второй таблицы. В результате получается набор данных, содержащий все возможные комбинации строк из обеих таблиц.
Для данного типа соединения не прописывается условие. В результате мы получаем выборку, количество строк в которой равно произведению количества строк в первой таблице на количество строк во второй таблице. Выборка содержит все возможные комбинации строк таблиц, что может быть полезным для нахождения всех возможных вариантов соединения записей.
Далее поговорим об использовании сочетаний различных соединений.
LEFT JOIN + LEFT JOIN
Использование двух и более LEFT JOIN при соединении с одной первой таблицей может помочь обогащать выборку данными, отсутствие или наличие которых независимо друг от друга. В примере с сотрудниками аналогично департаменту мы можем добавить данные о проекте, в котором работает сотрудник. Данные будут только для сотрудников, у которых указан проект, это никак не повлияет на департамент и количество итоговое количество строк.
Но что, если к департаменту привязан проект, и дополнительно нам нужна информация о проекте? Рассмотрим соединение нескольких таблиц между собой в выборке через LEFT JOIN.
SELECT
e.first_name,
e.last_name,
d.department_name,
p.project_name,
pd.detail AS project_detail
FROM
employees e
LEFT JOIN
departments d ON e.department_id = d.department_id
LEFT JOIN
projects p ON d.department_id = p.department_id
LEFT JOIN
project_details pd ON p.project_id = pd.project_id
В этом примере мы хотим получить список сотрудников с названиями их отделов, названиями проектов, над которыми они работают, и деталями этих проектов.
Используемые таблицы
Таблица employees (сотрудники):
employee_id | first_name | last_name | department_id |
1 | John | Doe | 10 |
2 | Jane | Smith | 20 |
3 | Emily | Davis | NULL |
4 | Michael | Brown | 30 |
Таблица departments (отделы):
department_id | department_name |
10 | HR |
20 | IT |
30 | Marketing |
40 | Sales |
Таблица projects (проекты):
project_id | project_name | department_id |
1 | Project Alpha | 10 |
2 | Project Beta | 20 |
3 | Project Gamma | 30 |
4 | Project Delta | 40 |
Таблица project_details (детали проектов):
project_id | detail |
1 | High Priority |
2 | Medium Priority |
Результат:
first_name | last_name | department_name | project_name | project_detail |
John | Doe | HR | Project Alpha | High Priority |
Jane | Smith | IT | Project Beta | Medium Priority |
Emily | Davis | NULL | NULL | NULL |
Michael | Brown | Marketing | Project Gamma | NULL |
Данный запрос позволяет получить полное представление о сотрудниках, их отделах, проектах и деталях проектов, над которыми они работают, даже если некоторые из этих связей отсутствуют. Это поможет помочь в сложных выборках, где важно обогащение данными, связи в которых могут отсутствовать.
RIGHT JOIN + RIGHT JOIN
Рассмотрим использование нескольких соединений RIGHT JOIN только с первой таблицей. Предположим, нам необходима выборка всех сотрудников. Также нужно дополнить выборку информацией о проектах сотрудников. Но необходимы только те проекты, которые привязаны к департаменту.
SELECT
e.first_name,
e.last_name,
d.department_name,
p.project_name
FROM
departments d
RIGHT JOIN
projects p ON d.department_id = p.department_id
RIGHT JOIN
employees e ON d.department_id = e.department_id
Используемые таблицы
Таблица employees (сотрудники):
employee_id | first_name | last_name | department_id |
1 | John | Doe | 10 |
2 | Jane | Smith | 20 |
3 | Emily | Davis | NULL |
4 | Michael | Brown | 30 |
Таблица departments (отделы):
department_id | department_name |
10 | HR |
20 | IT |
30 | Marketing |
40 | Sales |
Таблица projects (проекты):
project_id | project_name | department_id |
1 | Project Alpha | 10 |
2 | Project Beta | 20 |
3 | Project Gamma | NULL |
4 | Project Delta | 40 |
Результат:
first_name | last_name | department_name | project_name |
John | Doe | HR | Project Alpha |
Jane | Smith | IT | Project Beta |
Michael | Brown | NULL | NULL |
Emily | Davis | NULL | NULL |
Теперь давайте рассмотрим пример использование двух RIGHT JOIN последовательно с каждой таблицей. Предположим, мы хотим получить информацию о всех проектах, дополнив ее информацией о сотрудниках и департаментах. Необходимы только те сотрудники, которые участвуют в проектах, привязанных к департаменту. Как можно сделать это с использованием RIGHT JOIN.
SELECT
e.first_name,
e.last_name,
d.department_name,
p.project_name
FROM
employees e
RIGHT JOIN
departments d ON e.department_id = d.department_id
RIGHT JOIN
projects p ON d.department_id = p.department_id
Результат:
first_name | last_name | department_name | project_name |
John | Doe | HR | Project Alpha |
Jane | Smith | IT | Project Beta |
NULL | NULL | Sales | Project Delta |
NULL | NULL | NULL | Project Gamma |
Нужно отметить, что в итоговой выборке мы видим все проекты, так как это была самая «правая» таблица, как таблица сотрудников в предыдущей выборке. Также в последней выборке были взяты департаменты, к которым привязаны проект. В предыдущей выборке мы брали департаменты, где есть и проекты и сотрудники
LEFT JOIN + RIGHT JOIN
Предположим, нам необходима выборка всех проектов. Также необходимо получить информацию о департаментах, к которым принадлежат эти проекты, но только в том случае, если у данных департаментов есть сотрудники. Рассмотрим пример использования комбинаций различных соединений, на примере следующего запроса:
SELECT
e.first_name,
e.last_name,
d.department_name,
p.project_name
FROM
employees e
LEFT JOIN
departments d ON e.department_id = d.department_id
RIGHT JOIN
projects p ON d.department_id = p.department_id
Используемые таблицы
Таблица employees (сотрудники):
employee_id | first_name | last_name | department_id |
1 | John | Doe | 10 |
2 | Jane | Smith | 20 |
3 | Emily | Davis | NULL |
4 | Michael | Brown | 30 |
Таблица departments (отделы):
department_id | department_name |
10 | HR |
20 | IT |
30 | Marketing |
40 | Sales |
Таблица projects (проекты):
project_id | project_name | department_id |
1 | Project Alpha | 10 |
2 | Project Beta | 20 |
3 | Project Gamma | NULL |
4 | Project Delta | 40 |
Результат:
first_name | last_name | department_name | project_name |
John | Doe | HR | Project Alpha |
Jane | Smith | IT | Project Beta |
None | None | None | Project Gamma |
None | None | None | Project Delta |
В итоговой выборке мы видим все проекты и только два департамента, а также только двух сотрудников.
Подведем итоги
Мы рассмотрели примеры использования различных типов соединений, а также их комбинаций. В каждом примере мы получили разный результат. Соединения позволяют делать выборку гибкой для сложных условий и обогащения данными. Использование различных JOIN является эффективным инструментов, но нужно быть крайне внимательным при использовании комбинаций различных соединений.
Также делюсь скриптом на Python, с помощью которого можно попрактиковаться в запросах SQL без установки дополнительных программ и сложного развертывания базы данных.
GitHub — Oshurkova/PythonSQL: Работа с базой данных на Python
github.comУдачи в использовании оператора JOIN на практике!