Урок курса
INNER JOIN: соединение двух таблиц
SQL с нуля: бесплатный курс с практикойВ прошлом уроке мы разобрали, как внешний ключ связывает строки двух таблиц на уровне схемы. Теперь научимся использовать эту связь прямо в запросе — соединять таблицы так, чтобы получить данные из обеих сразу.
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.
Это же объясняет, почему такой запрос нельзя использовать для проверки «кто из клиентов ещё ничего не купил» — такие клиенты просто исчезнут из результата, не оставив никакого следа.
Попробуйте решить
Таблица 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;
Сколько строк вернёт этот запрос?
Продолжить с проверкой и прогрессом
Откройте интерактивный раннер с заданиями урока.
