Урок курса
INNER JOIN: соединение двух таблиц
SQL с нуля: бесплатный курс с практикойINNER JOIN: какой результат получится
Пусть в customers есть Анна (customer_id=1) и Борис (customer_id=2), а в orders — заказы 10 и 11, оба с customer_id=1. Запрос соединяет строки по равенству ключей:
SELECT c.name, o.order_id
FROM customers AS c
INNER JOIN orders AS o
ON c.customer_id = o.customer_id
ORDER BY o.order_id;Результат — две строки: Анна | 10 и Анна | 11. Бориса в выдаче нет: у него не нашлось заказа. Одна строка клиента может дать несколько строк результата, потому что совпадение проверяется для каждого заказа. Именно поэтому подсчёт клиентов через COUNT(*) после такого соединения считает заказы, а не уникальных людей. Для числа покупателей понадобится COUNT(DISTINCT c.customer_id).
Если нужен список всех клиентов, включая Бориса, используйте LEFT JOIN. Ниже урок подробно объясняет условие ON, псевдонимы и выбор столбцов из обеих таблиц.
В прошлом уроке мы разобрали, как внешний ключ связывает строки двух таблиц на уровне схемы. Теперь научимся использовать эту связь прямо в запросе — соединять таблицы так, чтобы получить данные из обеих сразу.
INNER JOIN: синтаксис ON и механизм совпадения строк
До этого момента каждый запрос работал с одной таблицей. Но данные в реляционной базе намеренно разнесены по разным таблицам: имена клиентов — в customers, их заказы — в orders. Чтобы получить и то и другое в одном результате, нужен JOIN.
Базовый синтаксис:
SELECT *
FROM customers
INNER JOIN orders ON customers.customer_id = orders.customer_id;
После INNER JOIN указывается вторая таблица, а после ON — условие: какие столбцы двух таблиц должны совпасть, чтобы строки объединились. Слово INNER можно опустить — просто JOIN без префикса работает точно так же, это синонимы.
Механизм такой: SQLite берёт каждую строку из customers и ищет в orders строки, у которых customer_id совпадает. Если совпадение нашлось — строки склеиваются в одну строку результата. Если нет — строка из customers в результат не попадает вообще.
Это ключевое свойство INNER JOIN: в результат включаются только строки, для которых нашлась пара в обеих таблицах. Ни «левая», ни «правая» таблица не имеет привилегий — если пары нет с любой стороны, строка отбрасывается.
Отсюда вытекает самый частый сюрприз для новичков: результат содержит меньше строк, чем ожидалось. Клиент есть в customers, но заказов не делал — и в результате JOIN его не будет. Заказ есть в orders, но customer_id там указывает на несуществующего клиента — этот заказ тоже пропадёт. Если видите неожиданно короткий результат, первым делом проверяйте именно это: у каждой ли строки есть пара в другой таблице.

Псевдоним таблицы через AS и уточнённое имя столбца
Когда в запросе участвуют две таблицы, сразу возникают две проблемы. Первая — длина: писать customers.customer_id и orders.customer_id в каждом условии утомительно. Вторая — неоднозначность: если столбец с одним именем есть в обеих таблицах, SQLite не поймёт, какой именно вы имеете в виду, и выдаст ошибку.
Оба вопроса решают псевдонимы таблиц:
SELECT *
FROM customers AS c
INNER JOIN orders AS o ON c.customer_id = o.customer_id;
AS c после имени таблицы объявляет псевдоним, который действует на весь запрос. Теперь везде — в ON, WHERE, SELECT — вместо customers пишем c, вместо orders — o.
Важное ограничение: после того как псевдоним объявлен, смешивать его с полным именем нельзя. Запись вроде c.customer_id = orders.customer_id вызовет ошибку, потому что orders уже «переименован» в o внутри этого запроса.
Запись псевдоним.столбец называется уточнённым именем столбца. Она нужна не только для сокращения, но и для явного указания источника. В SELECT это особенно важно: столбец customer_id есть в обеих таблицах, и если написать просто customer_id без уточнения, SQLite откажется выполнять запрос с сообщением об ambiguous column. Уточнение снимает неоднозначность:
SELECT c.name, o.order_date, o.total_amount
FROM customers AS c
INNER JOIN orders AS o ON c.customer_id = o.customer_id;
Здесь c.name — столбец name из customers, o.order_date и o.total_amount — столбцы из orders. Каждый столбец однозначно привязан к источнику, и запрос читается без двусмысленности.
Сквозной запрос: customers и orders с псевдонимами
Соберём всё вместе в рабочий запрос:
SELECT c.name AS клиент,
o.order_date AS дата,
o.total_amount AS сумма
FROM customers AS c
INNER JOIN orders AS o ON c.customer_id = o.customer_id;
Пройдём по каждому элементу:
FROM customers AS c— начинаем с таблицы клиентов и сразу даём ей псевдоним.INNER JOIN orders AS o— подключаем таблицу заказов с псевдонимомo.ON c.customer_id = o.customer_id— условие соединения: строки склеиваются, когдаcustomer_idсовпадает в обеих таблицах. Это именно та связь внешнего ключа, которую мы разбирали раньше:orders.customer_idссылается наcustomers.customer_id.SELECT c.name, o.order_date, o.total_amount— берём имя клиента из одной таблицы и данные заказа из другой. ПсевдонимыAS клиент,AS дата,AS суммадают столбцам результата читаемые заголовки — это тот жеAS, который использовался для псевдонимов столбцов ещё в ранних уроках.
Результат будет содержать по одной строке на каждый заказ, у которого есть соответствующий клиент. Если клиент сделал три заказа — в результате будет три строки с его именем. Если клиент не сделал ни одного заказа — его не будет совсем. Именно это отличает INNER JOIN от простого перечисления строк одной таблицы: результат формируется только там, где данные из двух таблиц пересекаются по условию ON.
Это же объясняет, почему такой запрос нельзя использовать для проверки «кто из клиентов ещё ничего не купил» — такие клиенты просто исчезнут из результата, не оставив никакого следа.
Задача 1
Таблица customers содержит 4 строки с customer_id: 1, 2, 3, 4. Таблица orders содержит 5 строк с customer_id: 1, 1, 2, 2, 3. Выполняется запрос:
SELECT c.customer_id, o.customer_id
FROM customers AS c
INNER JOIN orders AS o ON c.customer_id = o.customer_id;
Сколько строк вернёт этот запрос?
Задача 2
Какое утверждение об INNER JOIN всегда верно?
Задача 3
Какой запрос вернёт order_id и имя клиента только для заказов, у которых найден клиент с тем же customer_id?
Задача 4
Зачем при INNER JOIN двух таблиц использовать псевдонимы таблиц и уточнённые имена вида псевдоним.столбец?
Задача 5
Заказы с именами клиентов
В базе есть customers(customer_id, name) и orders(order_id, customer_id, order_date, total_amount). Напишите один SELECT, который через INNER JOIN сопоставляет заказ с клиентом и возвращает name, order_date, total_amount. Используйте алиасы c и o, а столбцы укажите через эти алиасы. Отсортируйте результат по дате заказа, а при одинаковой дате — по order_id по возрастанию. Клиенты без заказов и заказы без подходящего клиента в результат не входят.
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Продолжить с сохранением прогресса
Перейдите к уроку в учебном кабинете.
