Rusau LearnВойти

Базы данных

JOIN в SQL на примерах: INNER, LEFT и RIGHT JOIN

JOIN объединяет строки двух и более таблиц по общему полю, например заказы и покупателей по номеру покупателя. INNER JOIN оставляет только строки, для которых пара нашлась в обеих таблицах. LEFT JOIN выводит все строки левой таблицы, RIGHT JOIN – все строки правой, и там, где пары нет, в колонки другой таблицы ставится NULL. Тестировщик с помощью JOIN сверяет связанные данные, например ищет оплаченный заказ без платежа.

Мой видеоурок: Урок 22. Запросы Join в SQL/MySQL на примерах для тестировщикаОткрыть на YouTube
INNER, LEFT, RIGHT
три вида объединения, которые я разбираю
NULL
им LEFT и RIGHT JOIN заполняют колонки, где пары не нашлось
Бесплатно
урок с заданиями в моём базовом курсе

Что такое JOIN и зачем он тестировщику

В реляционной базе данные об одной покупке лежат в разных таблицах: покупатель в одной, заказ в другой, платёж в третьей. Таблицы связаны общими полями. У заказа, например, есть номер покупателя customer_id, и он совпадает с id в таблице покупателей. JOIN объединяет таблицы по такому полю, и в одной строке результата видно и заказ, и того, кто его сделал.

Тестировщику это нужно, чтобы проверять связи в данных. После оформления заказа запрос с JOIN показывает, к тому ли покупателю заказ привязан и появился ли у него платёж. Так же находят записи, у которых пары нет, например заказ без платежа или платёж по удалённому заказу.

По моему опыту, на собеседовании junior-тестировщика чаще всего просят написать обычный SELECT, а JOIN дают как задание со звёздочкой. Кто с ним справился, получает большой плюс, поэтому синтаксис стоит выучить заранее.

INNER, LEFT и RIGHT JOIN: в чём разница

Я разбираю три вида объединения и показываю их на схемах из двух пересекающихся кругов. Пример в уроке построен на двух таблицах учебной базы студентов: courses с факультетами и general с именем, фамилией, возрастом и полом. Общее поле у них id. В general есть строка с id 6, которой нет в courses, а в courses есть id 7, которого нет в general.

INNER JOIN выводит пять строк, где id совпали, и обе непарные строки в результат не попадают. LEFT JOIN с таблицей courses слева выводит шесть строк: к пяти добавляется id 7, и имя с фамилией у него равны NULL. RIGHT JOIN выводит все строки general, и в результате появляется id 6 с пустым факультетом.

  • INNER JOIN

    Внутреннее объединение выводит только строки, для которых пара нашлась в обеих таблицах. Слово INNER можно опустить, и просто JOIN означает то же самое.

  • LEFT JOIN

    Левое внешнее объединение выводит все строки таблицы, которая стоит после FROM. Если пары справа нет, колонки правой таблицы заполняются NULL. Полное название – LEFT OUTER JOIN.

  • RIGHT JOIN

    Правое внешнее объединение работает зеркально и сохраняет все строки таблицы, которая стоит после JOIN. Любой RIGHT JOIN можно переписать в LEFT JOIN, поменяв таблицы местами, поэтому в рабочих запросах чаще встречается LEFT.

  • FULL OUTER JOIN

    Полное объединение выводит все строки обеих таблиц и ставит NULL там, где пары нет. В MySQL его нет, и такой результат собирают из LEFT JOIN и RIGHT JOIN через UNION. В PostgreSQL оно есть, в SQLite появилось в версии 3.39.

Как написать запрос с JOIN

Запрос с JOIN – это обычный SELECT. После FROM стоит первая таблица, после JOIN вторая, а после ON условие, по которому сравниваются строки. Чаще всего это равенство ключей: первичного ключа одной таблицы и внешнего ключа другой. Например, SELECT orders.id, customers.name FROM orders INNER JOIN customers ON customers.id = orders.customer_id; выводит номер каждого заказа и имя покупателя.

Перед каждой колонкой лучше писать имя таблицы через точку. Там, где колонка с одним именем есть в обеих таблицах, без этого не обойтись: запрос с голым id упадёт с ошибкой о неоднозначном имени. В остальных местах префикс помогает читать запрос. Длинные имена сокращают псевдонимами, FROM orders o JOIN customers c, и дальше пишут o.id и c.name.

Ниже места, где в запросах с JOIN легко ошибиться.

  • Порядок таблиц

    Для INNER JOIN он не важен. Для LEFT и RIGHT JOIN от него зависит, строки какой таблицы попадут в результат целиком.

  • Условие на правую таблицу в WHERE

    LEFT JOIN заполняет колонки непарных строк значением NULL, а условие вроде WHERE o.status = 'cancelled' такие строки отбрасывает, и запрос незаметно превращается во внутреннее объединение. Если нужны все строки левой таблицы, условие переносят в ON. Исключение – условие IS NULL: оно, наоборот, оставляет только непарные строки.

  • Повторы строк

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

  • Больше двух таблиц

    JOIN можно писать подряд: FROM orders o JOIN customers c ON c.id = o.customer_id JOIN payments p ON p.order_id = o.id. Каждое следующее объединение добавляет колонки к уже собранному результату.

Пример: оплаченный заказ без платежа

В SQL-песочнице лежит учебная база интернет-магазина "Полка". Для примера нужны три её таблицы: customers с покупателями, orders с заказами, их статусом и суммой и payments с платежами, у каждого из которых есть номер заказа order_id. Задача из рабочего дня тестировщика – проверить, что у каждого заказа в статусе paid есть платёж. Все запросы ниже выполняются в песочнице как есть.

  • 1. Кто что заказал (INNER JOIN)

    SELECT o.id, c.name, o.status, o.amount FROM orders o INNER JOIN customers c ON c.id = o.customer_id; В результате 14 строк, по одной на заказ, и рядом с номером заказа стоит имя покупателя.

  • 2. Покупатели без заказов (LEFT JOIN и IS NULL)

    SELECT c.name, o.id AS order_id FROM customers c LEFT JOIN orders o ON o.customer_id = c.id WHERE o.id IS NULL; Остаётся один покупатель, Лев Панин, который зарегистрировался и ничего не заказал. Пустое значение ищут через IS NULL, потому что сравнение = NULL не находит ни одной строки.

  • 3. Оплаченные заказы без платежа

    SELECT o.id, o.status, o.amount, p.id AS payment_id FROM orders o LEFT JOIN payments p ON p.order_id = o.id WHERE o.status = 'paid' AND p.id IS NULL; Находится заказ 109 на 450: статус paid, а платежа в базе нет. Это дефект в данных, и в баг-репорт идут номер заказа и сам запрос, чтобы разработчик повторил проверку.

  • 4. То же через RIGHT JOIN

    SELECT o.id, o.status, p.id AS payment_id FROM payments p RIGHT JOIN orders o ON p.order_id = o.id ORDER BY o.id; Таблицы поменялись местами, и все заказы теперь справа. В результате 16 строк: у заказов 103, 109 и 113 платежа нет, а 107 и 111 выводятся дважды, потому что платежей у них по два.

  • 5. Списали больше суммы заказа (JOIN и GROUP BY)

    SELECT o.id, o.amount, SUM(p.amount) AS paid FROM orders o JOIN payments p ON p.order_id = o.id WHERE p.status = 'success' GROUP BY o.id, o.amount HAVING SUM(p.amount) > o.amount; Остаётся заказ 111: при сумме 3100 успешные платежи дали 6200, то есть деньги списали дважды. Такую проверку запускают после правок в оплате, например когда покупатель мог нажать "Оплатить" два раза подряд, и в баг-репорт идут номер заказа и номера обоих платежей, 10 и 11.

Вложенные подзапросы

Подзапрос – это SELECT внутри другого запроса. Его берут, когда нужный набор не получается выбрать одним SELECT с условиями: внутренний запрос находит значения, а внешний использует их в своём условии. Чаще всего подзапрос стоит в WHERE после IN. Мой совет для собеседования: если задачу не удаётся решить одним SELECT, в него можно вложить второй.

В уроке я строю подзапрос на таблице general. Внешний SELECT выводит имя, фамилию, пол и id, а внутренний отбирает строки, где id больше 1 и возраст не меньше 21. В результат попадают трое студентов. Признаю, пример не самый показательный, ведь такое условие уместилось бы и в обычный WHERE.

В базе песочницы подзапрос пригодится, например, чтобы найти покупателей с отменённым заказом. SELECT name FROM customers WHERE id IN (SELECT customer_id FROM orders WHERE status = 'cancelled'); Внутренний запрос вернёт номера покупателей, внешний их имена, Пётр Ильин и Игорь Лапин. Похожий результат даёт JOIN с условием на статус, но покупателя с двумя отменёнными заказами JOIN выведет дважды, а IN один раз.

Workbench, песочница и задания

В уроке я пишу запросы в MySQL Workbench, бесплатной программе с окном для запросов вместо командной строки. Её ставят тем же установщиком MySQL Installer, что и сервер: кнопка Add, раздел Applications, MySQL Workbench. Запрос в ней запускают значком молнии, а клавиша Enter только переносит строку. Workbench подчёркивает ошибки, подсказывает ключевые слова и имена таблиц и выполняет несколько запросов сразу, показывая результат каждого на своей вкладке.

Тренироваться удобно на W3Schools, где готовая база уже есть и ставить ничего не нужно. Запросы этой страницы выполняются в SQL-песочнице платформы. На YouTube двадцать второй урок завершает блок о базах данных в курсе "Тестировщик с нуля", а в бесплатном базовом курсе по тестированию есть урок на эту тему, и после теории в нём идут задания.

Шпаргалка по SQL собирает SELECT, WHERE, JOIN и GROUP BY с примерами, а в тренажёре запросы пишут на живых данных. На задачах из рабочего дня тестировщика SQL разбирает мой отдельный курс.

Курсы по теме

Вопросы и ответы

Что такое JOIN в SQL простыми словами?

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

Чем INNER JOIN отличается от LEFT JOIN?

INNER JOIN выводит только строки, для которых пара нашлась в обеих таблицах. LEFT JOIN выводит все строки левой таблицы, а где пары справа нет, ставит NULL в колонки правой. Поэтому LEFT JOIN с условием IS NULL находит записи без пары, например покупателей без заказов.

Чем LEFT JOIN отличается от RIGHT JOIN?

Тем, чьи строки сохраняются целиком. У LEFT JOIN это таблица после FROM, у RIGHT JOIN таблица после JOIN. Если поменять таблицы местами, RIGHT JOIN превращается в LEFT JOIN с теми же строками в результате.

Есть ли FULL OUTER JOIN в MySQL?

Нет. FULL OUTER JOIN выводит все строки обеих таблиц с NULL там, где пары нет, и в MySQL такой результат собирают из LEFT JOIN и RIGHT JOIN, соединённых через UNION. PostgreSQL поддерживает FULL OUTER JOIN напрямую, SQLite начиная с версии 3.39.

Спрашивают ли JOIN на собеседовании тестировщика?

Да. По моему опыту, чаще всего просят написать SELECT с условием, а JOIN дают как задание повышенной сложности, и кандидат, который с ним справился, заметно выделяется. Обычно дают две таблицы и просят вывести связанные данные или найти записи без пары.

Что такое вложенный подзапрос в SQL?

Это SELECT внутри другого запроса. Внутренний находит значения, а внешний использует их в условии, чаще всего через IN. Например, так находят имена покупателей, у которых есть отменённый заказ.

Посмотреть программы курсов

Программа каждого курса открыта целиком: видны все модули, уроки и число шагов.

Все курсы

Здесь используются cookies: без них сайт не работает, и по ним видно, откуда о платформе узнают.Cookies нужны для входа и учёта переходов. Подробнее