INNER JOIN — это способ соединить две таблицы по общему полю и оставить только те строки, для которых нашлась пара в обеих таблицах.
Чаще всего слово INNER даже не пишут. В SQL обычный JOIN почти всегда означает именно INNER JOIN.
Представь интернет-магазин. Есть таблица пользователей:
users — кто зарегистрировался на сайте;
orders — какие заказы были оформлены.
В таблице orders обычно не хранят имя покупателя. Там хранится только user_id — ссылка на пользователя из таблицы users.
Почему так? Чтобы не дублировать имя, email и другие данные пользователя в каждом заказе.
Но когда нам нужен отчёт «покупатель — сумма заказа», данные приходится собрать обратно. Вот здесь и нужен INNER JOIN.
Зачем нужен JOIN
В хорошей реляционной базе данные обычно разложены по разным таблицам.
Например:
- данные пользователя лежат в
users;
- заказы лежат в
orders;
- товары лежат в
products;
- категории товаров лежат в
categories;
- платежи лежат в
payments.
Так база не хранит одно и то же по сто раз.
Если пользователь сделал 20 заказов, его имя не нужно копировать в каждую строку заказа. Достаточно хранить имя один раз в users, а в orders записывать только user_id.
Но в реальных задачах почти всегда нужно смотреть данные вместе:
- имя пользователя и его заказы;
- товар и его категория;
- заказ и статус платежа;
- сотрудник и название отдела;
- книга и имя автора.
Для этого таблицы соединяют через JOIN.
Простая идея INNER JOIN
INNER JOIN ищет совпадения между двумя таблицами.
Например:
- в
users.id лежит идентификатор пользователя;
- в
orders.user_id лежит идентификатор пользователя, который сделал заказ.
Если users.id = orders.user_id, значит эти строки связаны.
SQL берёт строку пользователя, находит подходящие строки заказов и склеивает их в одну результирующую строку.
Если совпадения нет, строка не попадает в результат.
Это главное свойство INNER JOIN:
В результат попадают только те строки, у которых есть совпадение в обеих таблицах.
Базовый синтаксис
Общий вид такой:
SELECT
columns
FROM table_a
INNER JOIN table_b
ON table_a.key = table_b.key;
Слово INNER можно опустить:
SELECT
columns
FROM table_a
JOIN table_b
ON table_a.key = table_b.key;
Оба варианта означают одно и то же.
В реальном коде чаще пишут просто JOIN, потому что короче.
Пример с пользователями и заказами
Есть таблица users:
Есть таблица orders:
| id |
user_id |
amount |
created_at |
| 10 |
1 |
500 |
2026-03-01 |
| 11 |
1 |
1500 |
2026-03-05 |
| 12 |
2 |
200 |
2026-03-07 |
| 13 |
5 |
3000 |
2026-03-10 |
Нужно получить список заказов вместе с именем покупателя.
SELECT
users.name,
orders.amount,
orders.created_at
FROM users
INNER JOIN orders
ON users.id = orders.user_id;
Результат:
| name |
amount |
created_at |
| Anna |
500 |
2026-03-01 |
| Anna |
1500 |
2026-03-05 |
| Boris |
200 |
2026-03-07 |
Что произошло:
- у
Anna два заказа, поэтому она появилась в результате два раза;
- у
Boris один заказ, поэтому он появился один раз;
Vera и Gleb не попали в результат, потому что у них нет заказов;
- заказ с
user_id = 5 не попал в результат, потому что пользователя с id = 5 нет в users.
Это и есть поведение INNER JOIN: показывать только совпавшие пары.
Как SQL соединяет строки
Возьмём первую строку из users:
SQL смотрит в orders и ищет строки, где orders.user_id = 1.
Находит две строки:
| id |
user_id |
amount |
| 10 |
1 |
500 |
| 11 |
1 |
1500 |
Значит, в результате будет две строки:
| name |
amount |
| Anna |
500 |
| Anna |
1500 |
Теперь берём пользователя Boris с id = 2.
В orders есть один заказ с user_id = 2, поэтому будет одна строка.
А у Vera с id = 3 заказов нет. Совпадения нет — строки в результате тоже нет.
INNER JOIN — это только совпадения
Очень важно: INNER JOIN не показывает «одинокие» строки.
Если пользователь есть, но заказов нет, он не попадёт в результат.
Если заказ есть, но пользователя для него нет, этот заказ тоже не попадёт в результат.
Можно представить так:
| Ситуация |
Попадёт в результат INNER JOIN? |
| Пользователь есть и заказ есть |
Да |
| Пользователь есть, заказа нет |
Нет |
| Заказ есть, пользователя нет |
Нет |
Поэтому INNER JOIN удобно использовать, когда тебе нужны только связанные данные.
Например:
- только пользователи, у которых есть заказы;
- только товары, у которых есть категория;
- только платежи, у которых есть заказ;
- только книги, у которых есть автор в базе.
Если нужно показать пользователей даже без заказов, нужен уже не INNER JOIN, а LEFT JOIN.
Алиасы таблиц
В реальных запросах редко пишут полные имена таблиц каждый раз.
Вот так работает, но выглядит длинно:
SELECT
users.name,
orders.amount,
orders.created_at
FROM users
JOIN orders
ON users.id = orders.user_id;
Обычно используют алиасы — короткие имена таблиц:
SELECT
u.name,
o.amount,
o.created_at
FROM users u
JOIN orders o
ON u.id = o.user_id;
Здесь:
users u означает: таблицу users дальше называем u;
orders o означает: таблицу orders дальше называем o.
Логика запроса не меняется. Просто он становится короче и удобнее для чтения.
Особенно алиасы полезны, когда в запросе три, четыре или пять таблиц.
Зачем указывать имя таблицы перед колонкой
После JOIN в результате участвуют колонки сразу из нескольких таблиц.
Часто в разных таблицах есть одинаковые названия колонок.
Например, и в users, и в orders есть колонка id.
Если написать так:
SELECT
id,
name,
amount
FROM users
JOIN orders
ON users.id = orders.user_id;
база может не понять, какой именно id нужен: users.id или orders.id.
Поэтому лучше писать явно:
SELECT
u.id AS user_id,
o.id AS order_id,
u.name,
o.amount
FROM users u
JOIN orders o
ON u.id = o.user_id;
Так запрос читается без догадок:
u.id — идентификатор пользователя;
o.id — идентификатор заказа.
JOIN трёх таблиц
JOIN можно цеплять один за другим.
Допустим, у нас есть три таблицы:
users — пользователи;
orders — заказы;
products — товары.
Таблица users:
Таблица products:
| id |
name |
price |
| 100 |
Kettle |
4500 |
| 101 |
Phone |
90000 |
| 102 |
Book |
2000 |
Таблица orders:
| id |
user_id |
product_id |
amount |
| 10 |
1 |
100 |
4500 |
| 11 |
1 |
102 |
2000 |
| 12 |
2 |
100 |
4500 |
Нужно получить отчёт: кто что купил.
SELECT
u.name AS buyer,
p.name AS product,
o.amount
FROM users u
JOIN orders o
ON u.id = o.user_id
JOIN products p
ON p.id = o.product_id;
Результат:
| buyer |
product |
amount |
| Anna |
Kettle |
4500 |
| Anna |
Book |
2000 |
| Boris |
Kettle |
4500 |
Что произошло:
users соединилась с orders по пользователю.
- Потом результат соединился с
products по товару.
- На выходе получились строки, где есть и покупатель, и заказ, и товар.
Таких соединений может быть много. В реальных проектах запросы с несколькими JOIN — обычное дело.
JOIN и WHERE
JOIN соединяет таблицы.
WHERE фильтрует результат.
Например, нужно найти заказы Anna на сумму больше 1000.
SELECT
u.name,
o.amount,
o.created_at
FROM users u
JOIN orders o
ON u.id = o.user_id
WHERE u.name = 'Anna'
AND o.amount > 1000;
Здесь:
JOIN склеивает пользователей с заказами;
WHERE оставляет только строки, где имя Anna и сумма больше 1000.
Результат:
| name |
amount |
created_at |
| Anna |
1500 |
2026-03-05 |
В реальной аналитике запрос часто строится именно так:
- соединить нужные таблицы;
- отфильтровать лишние строки;
- отсортировать или посчитать агрегаты.
Условие в ON и условие в WHERE
После JOIN всегда есть важная часть — ON.
FROM users u
JOIN orders o
ON u.id = o.user_id
ON объясняет базе, как именно связаны таблицы.
Это не просто фильтр. Это правило соединения.
Если написать неправильное условие, результат будет неправильным.
Например, правильно:
SELECT
u.name,
o.amount
FROM users u
JOIN orders o
ON u.id = o.user_id;
А вот так почти наверняка ошибка:
SELECT
u.name,
o.amount
FROM users u
JOIN orders o
ON u.id = o.id;
Почему?
Потому что u.id — это идентификатор пользователя, а o.id — идентификатор заказа. Они могут случайно совпасть по числам, но логической связи между ними нет.
Правильная связь обычно такая:
первичный ключ одной таблицы соединяется с внешним ключом другой таблицы.
В нашем случае:
users.id — первичный ключ пользователя;
orders.user_id — внешний ключ на пользователя.
INNER JOIN и дубликаты
Новички часто удивляются: почему после JOIN стало больше строк?
Например, в таблице users пользователь Anna один:
Но в таблице orders у неё два заказа:
| id |
user_id |
amount |
| 10 |
1 |
500 |
| 11 |
1 |
1500 |
После соединения будет две строки:
| name |
amount |
| Anna |
500 |
| Anna |
1500 |
Это не ошибка.
JOIN показывает пары. Если у одной строки слева нашлось несколько строк справа, строк в результате тоже будет несколько.
Если тебе нужны не заказы, а уникальные пользователи, можно использовать DISTINCT:
SELECT DISTINCT
u.id,
u.name
FROM users u
JOIN orders o
ON u.id = o.user_id;
А если нужно посчитать количество заказов у каждого пользователя, нужен GROUP BY:
SELECT
u.id,
u.name,
COUNT(*) AS orders_count
FROM users u
JOIN orders o
ON u.id = o.user_id
GROUP BY u.id, u.name;
Результат:
| id |
name |
orders_count |
| 1 |
Anna |
2 |
| 2 |
Boris |
1 |
Пример: книги и авторы
Посмотрим на другой пример — библиотеку.
Таблица books:
| id |
title |
author_id |
| 1 |
War and Peace |
10 |
| 2 |
Anna Karenina |
10 |
| 3 |
Crime and Punishment |
11 |
| 4 |
The Idiot |
11 |
| 5 |
Unknown Book |
99 |
Таблица authors:
| id |
full_name |
country |
| 10 |
Leo Tolstoy |
Russia |
| 11 |
Fyodor Dostoevsky |
Russia |
| 12 |
Erich Maria Remarque |
Germany |
Нужно вывести книги вместе с именем автора и страной.
SELECT
b.title,
a.full_name AS author,
a.country
FROM books b
JOIN authors a
ON a.id = b.author_id
ORDER BY a.full_name, b.title;
Результат:
| title |
author |
country |
| Anna Karenina |
Leo Tolstoy |
Russia |
| War and Peace |
Leo Tolstoy |
Russia |
| Crime and Punishment |
Fyodor Dostoevsky |
Russia |
| The Idiot |
Fyodor Dostoevsky |
Russia |
Книга Unknown Book не попала в результат, потому что у неё author_id = 99, а автора с id = 99 нет в authors.
Автор Erich Maria Remarque тоже не попал, потому что в таблице books у него нет книг.
Так работает INNER JOIN: в результате остаются только совпадения.
INNER JOIN и связи один-ко-многим
Очень часто INNER JOIN используют для связи один-ко-многим.
Например:
- один пользователь может иметь много заказов;
- один автор может иметь много книг;
- одна категория может иметь много товаров;
- один отдел может иметь много сотрудников.
В такой связи одна строка из первой таблицы может соединиться с несколькими строками из второй таблицы.
Пример:
SELECT
c.name AS category,
p.name AS product
FROM categories c
JOIN products p
ON c.id = p.category_id;
Если в категории Books лежит 10 товаров, то категория Books появится в результате 10 раз — по одному разу рядом с каждым товаром.
Это нормальное поведение.
INNER JOIN и связи многие-ко-многим
Иногда между сущностями связь многие-ко-многим.
Например:
- один студент может ходить на много курсов;
- один курс может иметь много студентов.
Для такой связи обычно делают промежуточную таблицу.
Таблица students:
Таблица courses:
| id |
title |
| 10 |
SQL Basics |
| 20 |
Python Basics |
Таблица student_courses:
| student_id |
course_id |
| 1 |
10 |
| 1 |
20 |
| 2 |
10 |
Нужно получить список: студент — курс.
SELECT
s.name AS student,
c.title AS course
FROM students s
JOIN student_courses sc
ON sc.student_id = s.id
JOIN courses c
ON c.id = sc.course_id
ORDER BY s.name, c.title;
Результат:
| student |
course |
| Anna |
Python Basics |
| Anna |
SQL Basics |
| Boris |
SQL Basics |
Здесь student_courses — таблица-связка. Она помогает соединить студентов и курсы.
Старый синтаксис через WHERE
Иногда можно встретить старую запись без явного JOIN:
SELECT
u.name,
o.amount
FROM users u, orders o
WHERE u.id = o.user_id;
Она делает то же самое, что и:
SELECT
u.name,
o.amount
FROM users u
JOIN orders o
ON u.id = o.user_id;
Но современный вариант с JOIN ... ON лучше.
Почему?
Потому что он явно разделяет две вещи:
ON — как соединяются таблицы;
WHERE — какие строки нужно отфильтровать.
Такой запрос проще читать, проверять и поддерживать.
Для обучения и реальной работы лучше сразу привыкать к JOIN ... ON.
Что будет, если забыть ON
Одна из самых опасных ошибок — забыть условие соединения.
В некоторых базах такой запрос либо не выполнится, либо превратится в декартово произведение.
Декартово произведение означает: каждая строка из первой таблицы соединится с каждой строкой из второй таблицы.
Если в users 1000 строк, а в orders 10000 строк, результат может содержать 10 миллионов строк.
Обычно это не то, что ты хотел.
Правильный запрос:
SELECT
u.name,
o.amount
FROM users u
JOIN orders o
ON u.id = o.user_id;
Опасная идея:
SELECT
u.name,
o.amount
FROM users u
CROSS JOIN orders o;
CROSS JOIN нужен редко и осознанно. Для обычного соединения таблиц почти всегда нужен JOIN с условием ON.
Производительность INNER JOIN
Сам по себе JOIN — не что-то медленное или страшное.
Базы данных умеют хорошо соединять таблицы. Но им нужны нормальные условия и индексы.
Обычно индексируют колонки, по которым часто соединяют таблицы:
orders.user_id;
products.category_id;
books.author_id;
payments.order_id.
Например:
CREATE INDEX idx_orders_user_id
ON orders (user_id);
Такой индекс помогает базе быстрее находить заказы конкретного пользователя.
В реальных проектах внешние ключи и индексы на них — очень частая и важная история.
Когда использовать INNER JOIN
Используй INNER JOIN, когда тебе нужны только строки, у которых есть пара в другой таблице.
Например:
- заказы с существующими пользователями;
- товары с существующими категориями;
- книги с существующими авторами;
- платежи с существующими заказами;
- студенты, записанные хотя бы на один курс.
Хороший пример:
SELECT
u.name,
o.amount
FROM users u
JOIN orders o
ON u.id = o.user_id;
Этот запрос не пытается показать всех пользователей. Он показывает только пользователей с заказами.
Когда INNER JOIN не подходит
INNER JOIN не подходит, если нужно сохранить строки без пары.
Например:
Покажи всех пользователей, даже если они ничего не заказали.
Для такой задачи нужен LEFT JOIN.
Если использовать INNER JOIN, пользователи без заказов исчезнут из результата.
Сравни идею:
SELECT
u.name,
o.amount
FROM users u
JOIN orders o
ON u.id = o.user_id;
Этот запрос покажет только пользователей с заказами.
А вот такой запрос уже покажет всех пользователей:
SELECT
u.name,
o.amount
FROM users u
LEFT JOIN orders o
ON u.id = o.user_id;
У пользователей без заказов в колонках заказа будет NULL.
Частые ошибки новичков
Ошибка 1. Соединить не те колонки
Плохой вариант:
SELECT
u.name,
o.amount
FROM users u
JOIN orders o
ON u.id = o.id;
Здесь сравнивается идентификатор пользователя с идентификатором заказа. Обычно это бессмысленно.
Правильно:
SELECT
u.name,
o.amount
FROM users u
JOIN orders o
ON u.id = o.user_id;
Нужно соединять первичный ключ одной таблицы с внешним ключом другой.
Ошибка 2. Забыть уточнить, из какой таблицы колонка
Плохой вариант:
SELECT
id,
name,
amount
FROM users u
JOIN orders o
ON u.id = o.user_id;
Если id есть и в users, и в orders, база не поймёт, какой id нужен.
Лучше:
SELECT
u.id AS user_id,
o.id AS order_id,
u.name,
o.amount
FROM users u
JOIN orders o
ON u.id = o.user_id;
Ошибка 3. Удивляться дубликатам
Если у пользователя 5 заказов, после JOIN он появится 5 раз.
Это нормально.
Если нужны уникальные пользователи:
SELECT DISTINCT
u.id,
u.name
FROM users u
JOIN orders o
ON u.id = o.user_id;
Если нужно количество заказов:
SELECT
u.id,
u.name,
COUNT(*) AS orders_count
FROM users u
JOIN orders o
ON u.id = o.user_id
GROUP BY u.id, u.name;
Ошибка 4. Использовать INNER JOIN, когда нужен LEFT JOIN
Если задача звучит так:
Покажи всех пользователей и их заказы, если они есть.
INNER JOIN не подойдёт, потому что он уберёт пользователей без заказов.
Нужен LEFT JOIN.
SELECT
u.name,
o.amount
FROM users u
LEFT JOIN orders o
ON u.id = o.user_id;
Ошибка 5. Путать JOIN и WHERE
Плохой стиль:
SELECT
u.name,
o.amount
FROM users u, orders o
WHERE u.id = o.user_id;
Лучше современный синтаксис:
SELECT
u.name,
o.amount
FROM users u
JOIN orders o
ON u.id = o.user_id;
Так сразу видно, где условие соединения, а где обычная фильтрация.
Ошибка 6. Не понимать, почему исчезли строки
Если строка есть в одной таблице, но нет подходящей строки во второй, INNER JOIN её уберёт.
Это не ошибка базы. Это смысл INNER JOIN.
Например:
- пользователь без заказов исчезнет;
- заказ без пользователя исчезнет;
- книга без автора исчезнет;
- товар без категории исчезнет.
Если такие строки нужно сохранить, используй внешний join, чаще всего LEFT JOIN.
Как читать запрос с INNER JOIN
Возьмём запрос:
SELECT
u.name,
p.name AS product,
o.amount
FROM users u
JOIN orders o
ON o.user_id = u.id
JOIN products p
ON p.id = o.product_id
WHERE o.amount > 1000
ORDER BY o.amount DESC;
Читать его удобно так:
- Берём пользователей из
users.
- Соединяем их с заказами из
orders.
- Соединяем заказы с товарами из
products.
- Оставляем только заказы дороже 1000.
- Сортируем от самых дорогих к более дешёвым.
- Показываем имя пользователя, товар и сумму заказа.
Такой способ чтения помогает не теряться, когда запрос становится длиннее.
Главное из статьи
INNER JOIN соединяет две таблицы по общему условию и оставляет только совпавшие строки.
Базовый пример:
SELECT
u.name,
o.amount
FROM users u
JOIN orders o
ON u.id = o.user_id;
Главные правила:
INNER JOIN и просто JOIN обычно означают одно и то же.
- Условие соединения пишется после
ON.
- В результат попадают только строки, у которых есть совпадение в обеих таблицах.
- Если у пользователя нет заказов,
INNER JOIN его не покажет.
- Если заказ ссылается на несуществующего пользователя, такой заказ тоже не попадёт в результат.
- Алиасы вроде
u и o делают запрос короче и понятнее.
- Если в таблицах есть одинаковые имена колонок, указывай таблицу или алиас.
- Если у одной строки есть несколько совпадений, в результате будет несколько строк.
- Если нужны строки без пары, используй
LEFT JOIN.
- Если нужны колонки из нескольких связанных таблиц,
JOIN — основной инструмент.
Если сказать совсем просто: INNER JOIN — это способ собрать разложенные по разным таблицам данные обратно в понятную картину, но только там, где между строками действительно есть связь.
INNER JOIN— это способ соединить две таблицы по общему полю и оставить только те строки, для которых нашлась пара в обеих таблицах.Чаще всего слово
INNERдаже не пишут. В SQL обычныйJOINпочти всегда означает именноINNER JOIN.Представь интернет-магазин. Есть таблица пользователей:
users— кто зарегистрировался на сайте;orders— какие заказы были оформлены.В таблице
ordersобычно не хранят имя покупателя. Там хранится толькоuser_id— ссылка на пользователя из таблицыusers.Почему так? Чтобы не дублировать имя, email и другие данные пользователя в каждом заказе.
Но когда нам нужен отчёт «покупатель — сумма заказа», данные приходится собрать обратно. Вот здесь и нужен
INNER JOIN.Зачем нужен JOIN
В хорошей реляционной базе данные обычно разложены по разным таблицам.
Например:
users;orders;products;categories;payments.Так база не хранит одно и то же по сто раз.
Если пользователь сделал 20 заказов, его имя не нужно копировать в каждую строку заказа. Достаточно хранить имя один раз в
users, а вordersзаписывать толькоuser_id.Но в реальных задачах почти всегда нужно смотреть данные вместе:
Для этого таблицы соединяют через
JOIN.Простая идея INNER JOIN
INNER JOINищет совпадения между двумя таблицами.Например:
users.idлежит идентификатор пользователя;orders.user_idлежит идентификатор пользователя, который сделал заказ.Если
users.id = orders.user_id, значит эти строки связаны.SQL берёт строку пользователя, находит подходящие строки заказов и склеивает их в одну результирующую строку.
Если совпадения нет, строка не попадает в результат.
Это главное свойство
INNER JOIN:Базовый синтаксис
Общий вид такой:
SELECT columns FROM table_a INNER JOIN table_b ON table_a.key = table_b.key;Слово
INNERможно опустить:SELECT columns FROM table_a JOIN table_b ON table_a.key = table_b.key;Оба варианта означают одно и то же.
В реальном коде чаще пишут просто
JOIN, потому что короче.Пример с пользователями и заказами
Есть таблица
users:Есть таблица
orders:Нужно получить список заказов вместе с именем покупателя.
SELECT users.name, orders.amount, orders.created_at FROM users INNER JOIN orders ON users.id = orders.user_id;Результат:
Что произошло:
Annaдва заказа, поэтому она появилась в результате два раза;Borisодин заказ, поэтому он появился один раз;VeraиGlebне попали в результат, потому что у них нет заказов;user_id = 5не попал в результат, потому что пользователя сid = 5нет вusers.Это и есть поведение
INNER JOIN: показывать только совпавшие пары.Как SQL соединяет строки
Возьмём первую строку из
users:SQL смотрит в
ordersи ищет строки, гдеorders.user_id = 1.Находит две строки:
Значит, в результате будет две строки:
Теперь берём пользователя
Borisсid = 2.В
ordersесть один заказ сuser_id = 2, поэтому будет одна строка.А у
Veraсid = 3заказов нет. Совпадения нет — строки в результате тоже нет.INNER JOIN — это только совпадения
Очень важно:
INNER JOINне показывает «одинокие» строки.Если пользователь есть, но заказов нет, он не попадёт в результат.
Если заказ есть, но пользователя для него нет, этот заказ тоже не попадёт в результат.
Можно представить так:
INNER JOIN?Поэтому
INNER JOINудобно использовать, когда тебе нужны только связанные данные.Например:
Если нужно показать пользователей даже без заказов, нужен уже не
INNER JOIN, аLEFT JOIN.Алиасы таблиц
В реальных запросах редко пишут полные имена таблиц каждый раз.
Вот так работает, но выглядит длинно:
SELECT users.name, orders.amount, orders.created_at FROM users JOIN orders ON users.id = orders.user_id;Обычно используют алиасы — короткие имена таблиц:
SELECT u.name, o.amount, o.created_at FROM users u JOIN orders o ON u.id = o.user_id;Здесь:
users uозначает: таблицуusersдальше называемu;orders oозначает: таблицуordersдальше называемo.Логика запроса не меняется. Просто он становится короче и удобнее для чтения.
Особенно алиасы полезны, когда в запросе три, четыре или пять таблиц.
Зачем указывать имя таблицы перед колонкой
После
JOINв результате участвуют колонки сразу из нескольких таблиц.Часто в разных таблицах есть одинаковые названия колонок.
Например, и в
users, и вordersесть колонкаid.Если написать так:
SELECT id, name, amount FROM users JOIN orders ON users.id = orders.user_id;база может не понять, какой именно
idнужен:users.idилиorders.id.Поэтому лучше писать явно:
SELECT u.id AS user_id, o.id AS order_id, u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id;Так запрос читается без догадок:
u.id— идентификатор пользователя;o.id— идентификатор заказа.JOIN трёх таблиц
JOINможно цеплять один за другим.Допустим, у нас есть три таблицы:
users— пользователи;orders— заказы;products— товары.Таблица
users:Таблица
products:Таблица
orders:Нужно получить отчёт: кто что купил.
SELECT u.name AS buyer, p.name AS product, o.amount FROM users u JOIN orders o ON u.id = o.user_id JOIN products p ON p.id = o.product_id;Результат:
Что произошло:
usersсоединилась сordersпо пользователю.productsпо товару.Таких соединений может быть много. В реальных проектах запросы с несколькими
JOIN— обычное дело.JOIN и WHERE
JOINсоединяет таблицы.WHEREфильтрует результат.Например, нужно найти заказы
Annaна сумму больше 1000.SELECT u.name, o.amount, o.created_at FROM users u JOIN orders o ON u.id = o.user_id WHERE u.name = 'Anna' AND o.amount > 1000;Здесь:
JOINсклеивает пользователей с заказами;WHEREоставляет только строки, где имяAnnaи сумма больше 1000.Результат:
В реальной аналитике запрос часто строится именно так:
Условие в ON и условие в WHERE
После
JOINвсегда есть важная часть —ON.FROM users u JOIN orders o ON u.id = o.user_idONобъясняет базе, как именно связаны таблицы.Это не просто фильтр. Это правило соединения.
Если написать неправильное условие, результат будет неправильным.
Например, правильно:
SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id;А вот так почти наверняка ошибка:
SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.id;Почему?
Потому что
u.id— это идентификатор пользователя, аo.id— идентификатор заказа. Они могут случайно совпасть по числам, но логической связи между ними нет.Правильная связь обычно такая:
В нашем случае:
users.id— первичный ключ пользователя;orders.user_id— внешний ключ на пользователя.INNER JOIN и дубликаты
Новички часто удивляются: почему после
JOINстало больше строк?Например, в таблице
usersпользовательAnnaодин:Но в таблице
ordersу неё два заказа:После соединения будет две строки:
Это не ошибка.
JOINпоказывает пары. Если у одной строки слева нашлось несколько строк справа, строк в результате тоже будет несколько.Если тебе нужны не заказы, а уникальные пользователи, можно использовать
DISTINCT:SELECT DISTINCT u.id, u.name FROM users u JOIN orders o ON u.id = o.user_id;А если нужно посчитать количество заказов у каждого пользователя, нужен
GROUP BY:SELECT u.id, u.name, COUNT(*) AS orders_count FROM users u JOIN orders o ON u.id = o.user_id GROUP BY u.id, u.name;Результат:
Пример: книги и авторы
Посмотрим на другой пример — библиотеку.
Таблица
books:Таблица
authors:Нужно вывести книги вместе с именем автора и страной.
SELECT b.title, a.full_name AS author, a.country FROM books b JOIN authors a ON a.id = b.author_id ORDER BY a.full_name, b.title;Результат:
Книга
Unknown Bookне попала в результат, потому что у неёauthor_id = 99, а автора сid = 99нет вauthors.Автор
Erich Maria Remarqueтоже не попал, потому что в таблицеbooksу него нет книг.Так работает
INNER JOIN: в результате остаются только совпадения.INNER JOIN и связи один-ко-многим
Очень часто
INNER JOINиспользуют для связи один-ко-многим.Например:
В такой связи одна строка из первой таблицы может соединиться с несколькими строками из второй таблицы.
Пример:
SELECT c.name AS category, p.name AS product FROM categories c JOIN products p ON c.id = p.category_id;Если в категории
Booksлежит 10 товаров, то категорияBooksпоявится в результате 10 раз — по одному разу рядом с каждым товаром.Это нормальное поведение.
INNER JOIN и связи многие-ко-многим
Иногда между сущностями связь многие-ко-многим.
Например:
Для такой связи обычно делают промежуточную таблицу.
Таблица
students:Таблица
courses:Таблица
student_courses:Нужно получить список: студент — курс.
SELECT s.name AS student, c.title AS course FROM students s JOIN student_courses sc ON sc.student_id = s.id JOIN courses c ON c.id = sc.course_id ORDER BY s.name, c.title;Результат:
Здесь
student_courses— таблица-связка. Она помогает соединить студентов и курсы.Старый синтаксис через WHERE
Иногда можно встретить старую запись без явного
JOIN:SELECT u.name, o.amount FROM users u, orders o WHERE u.id = o.user_id;Она делает то же самое, что и:
SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id;Но современный вариант с
JOIN ... ONлучше.Почему?
Потому что он явно разделяет две вещи:
ON— как соединяются таблицы;WHERE— какие строки нужно отфильтровать.Такой запрос проще читать, проверять и поддерживать.
Для обучения и реальной работы лучше сразу привыкать к
JOIN ... ON.Что будет, если забыть ON
Одна из самых опасных ошибок — забыть условие соединения.
В некоторых базах такой запрос либо не выполнится, либо превратится в декартово произведение.
Декартово произведение означает: каждая строка из первой таблицы соединится с каждой строкой из второй таблицы.
Если в
users1000 строк, а вorders10000 строк, результат может содержать 10 миллионов строк.Обычно это не то, что ты хотел.
Правильный запрос:
SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id;Опасная идея:
SELECT u.name, o.amount FROM users u CROSS JOIN orders o;CROSS JOINнужен редко и осознанно. Для обычного соединения таблиц почти всегда нуженJOINс условиемON.Производительность INNER JOIN
Сам по себе
JOIN— не что-то медленное или страшное.Базы данных умеют хорошо соединять таблицы. Но им нужны нормальные условия и индексы.
Обычно индексируют колонки, по которым часто соединяют таблицы:
orders.user_id;products.category_id;books.author_id;payments.order_id.Например:
CREATE INDEX idx_orders_user_id ON orders (user_id);Такой индекс помогает базе быстрее находить заказы конкретного пользователя.
В реальных проектах внешние ключи и индексы на них — очень частая и важная история.
Когда использовать INNER JOIN
Используй
INNER JOIN, когда тебе нужны только строки, у которых есть пара в другой таблице.Например:
Хороший пример:
SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id;Этот запрос не пытается показать всех пользователей. Он показывает только пользователей с заказами.
Когда INNER JOIN не подходит
INNER JOINне подходит, если нужно сохранить строки без пары.Например:
Для такой задачи нужен
LEFT JOIN.Если использовать
INNER JOIN, пользователи без заказов исчезнут из результата.Сравни идею:
SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id;Этот запрос покажет только пользователей с заказами.
А вот такой запрос уже покажет всех пользователей:
SELECT u.name, o.amount FROM users u LEFT JOIN orders o ON u.id = o.user_id;У пользователей без заказов в колонках заказа будет
NULL.Частые ошибки новичков
Ошибка 1. Соединить не те колонки
Плохой вариант:
SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.id;Здесь сравнивается идентификатор пользователя с идентификатором заказа. Обычно это бессмысленно.
Правильно:
SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id;Нужно соединять первичный ключ одной таблицы с внешним ключом другой.
Ошибка 2. Забыть уточнить, из какой таблицы колонка
Плохой вариант:
SELECT id, name, amount FROM users u JOIN orders o ON u.id = o.user_id;Если
idесть и вusers, и вorders, база не поймёт, какойidнужен.Лучше:
SELECT u.id AS user_id, o.id AS order_id, u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id;Ошибка 3. Удивляться дубликатам
Если у пользователя 5 заказов, после
JOINон появится 5 раз.Это нормально.
Если нужны уникальные пользователи:
SELECT DISTINCT u.id, u.name FROM users u JOIN orders o ON u.id = o.user_id;Если нужно количество заказов:
SELECT u.id, u.name, COUNT(*) AS orders_count FROM users u JOIN orders o ON u.id = o.user_id GROUP BY u.id, u.name;Ошибка 4. Использовать INNER JOIN, когда нужен LEFT JOIN
Если задача звучит так:
INNER JOINне подойдёт, потому что он уберёт пользователей без заказов.Нужен
LEFT JOIN.SELECT u.name, o.amount FROM users u LEFT JOIN orders o ON u.id = o.user_id;Ошибка 5. Путать JOIN и WHERE
Плохой стиль:
SELECT u.name, o.amount FROM users u, orders o WHERE u.id = o.user_id;Лучше современный синтаксис:
SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id;Так сразу видно, где условие соединения, а где обычная фильтрация.
Ошибка 6. Не понимать, почему исчезли строки
Если строка есть в одной таблице, но нет подходящей строки во второй,
INNER JOINеё уберёт.Это не ошибка базы. Это смысл
INNER JOIN.Например:
Если такие строки нужно сохранить, используй внешний join, чаще всего
LEFT JOIN.Как читать запрос с INNER JOIN
Возьмём запрос:
SELECT u.name, p.name AS product, o.amount FROM users u JOIN orders o ON o.user_id = u.id JOIN products p ON p.id = o.product_id WHERE o.amount > 1000 ORDER BY o.amount DESC;Читать его удобно так:
users.orders.products.Такой способ чтения помогает не теряться, когда запрос становится длиннее.
Главное из статьи
INNER JOINсоединяет две таблицы по общему условию и оставляет только совпавшие строки.Базовый пример:
SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id;Главные правила:
INNER JOINи простоJOINобычно означают одно и то же.ON.INNER JOINего не покажет.uиoделают запрос короче и понятнее.LEFT JOIN.JOIN— основной инструмент.Если сказать совсем просто:
INNER JOIN— это способ собрать разложенные по разным таблицам данные обратно в понятную картину, но только там, где между строками действительно есть связь.