SQLJOININNER JOINtutorial

Что такое INNER JOIN в SQL? Соединение таблиц для начинающих

INNER JOIN соединяет данные из двух таблиц по общему полю. Самый базовый и самый частый JOIN. Разбираем синтаксис, ON-условие, многотабличные JOIN, частые ошибки и три задачи на тренировку.

6 мин чтенияСправочникSQL · JOIN · INNER JOIN · tutorial · beginner

INNER JOIN (или просто JOIN) — это команда для соединения двух таблиц по общему полю. Самый базовый и самый частый тип JOIN, без него в реляционных БД никуда.

Простая аналогия. У тебя есть две таблицы:

  • users — пользователи (id, name)
  • orders — заказы (id, user_id, amount)

В orders нет имени покупателя — там только user_id. Чтобы получить пары «имя покупателя — сумма заказа», нужно «склеить» эти таблицы по общему полю. Этот общий ключ — users.id и orders.user_id. И склейка делается через INNER JOIN.

Зачем нужен JOIN

В нормальной БД данные разбиты по таблицам, чтобы не дублировать информацию. Имя пользователя хранится только в users. В orders — только user_id, ссылающийся на пользователя. Это называется нормализация.

Когда нужно показать «имя + заказ» — приходится собирать обратно. Для этого и есть JOIN. Это одна из самых важных тем SQL — без понимания JOIN ты не напишешь ни одного реального запроса в проекте.

Базовый синтаксис

SELECT колонки
FROM таблица_A
INNER JOIN таблица_B
  ON таблица_A.ключ = таблица_B.ключ;

Слово INNER можно опускать — JOIN без префикса означает именно INNER JOIN. Часто пишут просто JOIN.

Пример: магазин

Таблица users:

id name email
1 Анна anna@example.com
2 Борис bob@example.com
3 Вера vera@example.com
4 Григорий grisha@example.com

Таблица orders:

id user_id amount created_at
10 1 500 2024-03-01
11 1 1500 2024-03-05
12 2 200 2024-03-07
13 5 3000 2024-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
Анна 500 2024-03-01
Анна 1500 2024-03-05
Борис 200 2024-03-07

Что произошло:

  • Каждая строка из users спарилась с каждой строкой из orders, у которой users.id = orders.user_id.
  • У Анны два заказа → две строки в результате, имя дублируется.
  • У Бориса один заказ → одна строка.
  • Вера и Григорий не попали — у них нет заказов в orders. Это и есть особенность INNER JOIN: только пары, у которых есть совпадение в обеих таблицах.
  • Заказ #13 не попалuser_id = 5 не существует в users. Так бывает, например, когда пользователь удалён, а заказ остался.

Если хочется тоже показать пользователей без заказов — это уже не INNER JOIN, а LEFT JOIN (отдельная статья).

Алиасы — короткие имена таблиц

Каждый раз писать users.name, orders.amount — длинно. Поэтому в реальном коде используют алиасы:

SELECT u.name, o.amount, o.created_at
FROM users u
INNER JOIN orders o
  ON u.id = o.user_id;

users u означает «дай таблице users псевдоним u». Это ничего не меняет в логике — просто короче. На больших запросах с тремя-пятью таблицами без алиасов читать невозможно.

Примечание: алиасы становятся обязательными, если в двух таблицах есть колонки с одинаковыми именами. Иначе база не поймёт, чей id ты имеешь в виду.

JOIN трёх и более таблиц

JOIN-ы можно цеплять цепочкой: A JOIN B JOIN C. Например, добавим таблицу products:

id name price
100 Чайник Bosch 4500
101 iPhone 15 90000
102 Книга «Чистый код» 2000

И изменим orders, добавив product_id:

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
INNER JOIN orders o   ON u.id = o.user_id
INNER JOIN products p ON p.id = o.product_id;

Результат:

buyer product amount
Анна Чайник Bosch 4500
Анна Книга «Чистый код» 2000
Борис Чайник Bosch 4500

Как это работает:

  1. Сначала база соединяет users и orders по user_id.
  2. Потом — результат соединения с products по product_id.
  3. На выходе — три строки, где в обеих JOIN-парах есть совпадения.

Таких цепочек может быть сколько угодно. Запрос «пользователи, их заказы, товары в заказах, категории товаров и магазины» — это пять JOIN-ов подряд.

JOIN с условием WHERE

JOIN обычно идёт вместе с WHERE — JOIN склеивает таблицы, WHERE фильтрует результат. Например, заказы Анны на сумму больше 1000:

SELECT u.name, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.name = 'Анна'
  AND o.amount > 1000;

Крайне частая комбинация — почти любой бизнес-запрос состоит из «соединить таблицы JOIN-ом + отфильтровать WHERE-ом + посчитать агрегат».

INNER JOIN — это пересечение

Если мыслить теорией множеств: INNER JOIN — это пересечение двух таблиц по ключу. В результат попадает только то, что есть И там, И там.

users       orders
  ┌──┐      ┌──┐
  │  │      │  │
  │ ●│ ←  → │● │  ← пары: только эти попадают в результат
  │  │      │  │
  └──┘      └──┘

Если у пользователя нет заказов — он не в результате. Если у заказа нет пользователя (FK битый) — заказ не в результате.

Большой пример: библиотека

books:

id title author_id
1 Война и мир 10
2 Анна Каренина 10
3 Преступление и наказание 11
4 Идиот 11
5 (без автора в БД) 99

authors:

id full_name country
10 Лев Толстой Россия
11 Фёдор Достоевский Россия
12 Эрих Мария Ремарк Германия

Запрос — список всех книг с именем автора и страной:

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
Война и мир Лев Толстой Россия
Анна Каренина Лев Толстой Россия
Идиот Фёдор Достоевский Россия
Преступление и наказание Фёдор Достоевский Россия

«(без автора в БД)» не попала — у неё author_id = 99, а в authors такого нет. Ремарка не попал — у него нет ни одной книги в нашей books. INNER JOIN отфильтровал и тех, и других.

Частые ошибки новичков

1. Забыть условие ON. Если написать JOIN orders без ON, многие БД либо ругнутся, либо сделают CROSS JOIN — декартово произведение всех строк (миллион × миллион). Это либо ошибка, либо очень-очень медленный запрос.

2. Условие в ON не на тех колонках. ON u.id = o.id вместо ON u.id = o.user_id — частая опечатка. Запрос не упадёт, но вернёт ерунду — сравнятся user.id = order.id (то есть пользователь №5 склеится с заказом №5, что бессмысленно).

3. Двусмысленные имена колонок. Если и users, и orders имеют колонку id, и ты пишешь SELECT id, name FROM users JOIN orders ON … — будет ошибка «column reference "id" is ambiguous». Используй users.id или алиасы.

4. Дубликаты в результате. Если у пользователя 5 заказов, в результате будет 5 строк с одним и тем же именем. Это не баг JOIN-а, а его суть. Нужны уникальные пользователи — оборачивай в SELECT DISTINCT u.name или комбинируй с GROUP BY.

5. Думать, что JOIN сам по себе медленный. Сами по себе JOIN-ы быстрые, если есть индексы на колонках, по которым джойнят. Поэтому user_id и подобные FK-колонки в продакшене всегда индексируют.

6. Путать JOIN и WHERE. Технически в старом синтаксисе можно было писать FROM users, orders WHERE users.id = orders.user_id. Так делать не надо — современный синтаксис JOIN ... ON ... читается лучше и явно различает фильтрацию и соединение.

Мини-резюме

  • INNER JOIN соединяет две таблицы по общему ключу.
  • В результат попадают только пары, у которых есть совпадение в обеих таблицах.
  • Условие соединения — после ON.
  • Слово INNER опционально, обычно пишут просто JOIN.
  • Можно цеплять много JOIN-ов подряд (трёх- и пятитабличные запросы — норма).
  • Для удобства используй алиасы (users u).
  • Если нужны и пользователи без заказов — это LEFT JOIN, отдельный сценарий.

Закрепи на практике

Решай задачи в SQL-тренажёре с мгновенной проверкой и подсказками.

Открыть тренажёр