JOIN

JOIN нужен, чтобы объединять строки из двух (и более) таблиц по условию связи. Обычно условие связи выглядит так: ON a.id = b.a_id.

Что запомнить

В JOIN всегда есть две части: тип соединения (INNER/LEFT/...) и условие ON. Большинство ошибок — неправильный ON или неправильный тип JOIN.


Пример 1 — INNER JOIN

INNER JOIN возвращает только те строки, где нашлась пара по условию ON. Если пары нет — строка не попадёт в результат.

Данные:

users
idname
1Alice
2Bob
3Carol
orders
iduser_idtotalstatus
101120done
11180new
12250done
13NULL70new

Запрос:

SELECT u.name, o.id AS order_id, o.total, o.status
FROM users u
JOIN orders o ON o.user_id = u.id
ORDER BY u.id, o.id;
Результат:
nameorder_idtotalstatus
Alice10120done
Alice1180new
Bob1250done
Почему order 13 не попал?

Потому что orders.user_id = NULL, а сравнение NULL = ... никогда не истинно. Совпадения по ON o.user_id = u.id нет.


Пример 2 — LEFT JOIN

LEFT JOIN возвращает все строки из левой таблицы и присоединяет совпадения справа. Если совпадений нет — поля справа будут NULL.

Данные:

users
idname
1Alice
2Bob
3Carol
orders
iduser_idtotalstatus
101120done
11180new
12250done
13NULL70new

Запрос:

SELECT u.name, o.id AS order_id, o.total
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
ORDER BY u.id, o.id;
Результат:
nameorder_idtotal
Alice10120
Alice1180
Bob1250
CarolNULLNULL

Пример 3 — ON vs WHERE в LEFT JOIN

Это самая частая ловушка: фильтрация по правой таблице в WHERE “убивает” строки без совпадений.

3.1 Условие в WHERE (плохой вариант, если “все слева должны остаться”)

Данные:

users
idname
1Alice
2Bob
3Carol
orders
iduser_idtotalstatus
101120done
11180new
12250done
13NULL70new

Запрос:

SELECT u.name, o.id, o.status
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE o.status = 'done'
ORDER BY u.id, o.id;
Результат:
nameidstatus
Alice10done
Bob12done
⚠ Почему исчезла Carol?

У Carol нет заказов → справа поля NULL. Условие o.status = 'done' ложно для NULL → строка отфильтровалась.

3.2 Условие в ON (правильный вариант)

Данные:

users
idname
1Alice
2Bob
3Carol
orders
iduser_idtotalstatus
101120done
11180new
12250done
13NULL70new

Запрос:

SELECT u.name, o.id, o.status
FROM users u
LEFT JOIN orders o
  ON o.user_id = u.id
 AND o.status = 'done'
ORDER BY u.id, o.id;
Результат:
nameidstatus
Alice10done
Bob12done
CarolNULLNULL

Пример 4 — “все справа” (аналог RIGHT JOIN)

В SQLite проще мыслить так: если нужно “все строки из orders + пользователь, если есть” — просто делай orders левой таблицей в LEFT JOIN.

Данные:

users
idname
1Alice
2Bob
3Carol
orders
iduser_idtotalstatus
101120done
11180new
12250done
13NULL70new

Запрос:

SELECT o.id, o.total, u.name
FROM orders o
LEFT JOIN users u ON u.id = o.user_id
ORDER BY o.id;
Результат:
idtotalname
10120Alice
1180Alice
1250Bob
1370NULL

Пример 5 — CROSS JOIN

CROSS JOIN соединяет каждую строку A с каждой строкой B (декартово произведение). Используется редко, но важно знать, чтобы случайно не получить огромный результат.

Данные:

users
idname
1Alice
2Bob
statuses
status
new
done

Запрос:

SELECT u.name, s.status
FROM users u
CROSS JOIN statuses s
ORDER BY u.id, s.status;
Результат:
namestatus
Alicedone
Alicenew
Bobdone
Bobnew
⚠ Осторожно

При больших таблицах результат растёт как произведение размеров. В тренажёре такие запросы могут сильно грузить сервер.


Пример 6 — JOIN + агрегаты (GROUP BY)

Частый реальный кейс: посчитать количество заказов и сумму по каждому пользователю.

Данные:

users
idname
1Alice
2Bob
3Carol
orders
iduser_idtotal
101120
11180
12250

Запрос:

SELECT
  u.name,
  COUNT(o.id) AS orders_count,
  COALESCE(SUM(o.total), 0) AS total_sum
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.id, u.name
ORDER BY u.id;
Результат:
nameorders_counttotal_sum
Alice2200
Bob150
Carol00
💡 Почему COALESCE?

Для пользователя без заказов SUM(o.total) будет NULL, поэтому превращаем в 0: COALESCE(SUM(...), 0).


Пример 7 — Anti-join (найти “без пары”)

Задача: найти пользователей, у которых нет заказов. Делается через LEFT JOIN и проверку IS NULL по ключу правой таблицы.

Данные:

users
idname
1Alice
2Bob
3Carol
orders
iduser_idtotal
101120
11180
12250

Запрос:

SELECT u.name
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE o.id IS NULL;
Результат:
name
Carol

Пример 8 — Self JOIN (таблица соединяется сама с собой)

Полезно для иерархий: сотрудники и их менеджеры.

Данные:

employees
idnamemanager_id
1AliceNULL
2Bob1
3Carol1
4Dave2

Запрос:

SELECT e.name AS employee, m.name AS manager
FROM employees e
LEFT JOIN employees m ON m.id = e.manager_id
ORDER BY e.id;
Результат:
employeemanager
AliceNULL
BobAlice
CarolAlice
DaveBob

Частые ошибки при JOIN

⚠ Ошибки новичков
  • Неправильное условие связи (ON не по ключам) → “лишние” строки или пустой результат.
  • Забыть алиасы при одинаковых названиях столбцов → непонятно, из какой таблицы поле.
  • Фильтровать правую таблицу через WHERE при LEFT JOIN → превращение в INNER JOIN.
  • Случайный CROSS JOIN → взрыв количества строк и тормоза.
  • Дубликаты при JOIN: если справа несколько строк на одну слева — строк станет больше (это не баг).

Итого