← Назад к вопросам

В чем разница между INNER JOIN и OUTER JOIN?

2.2 Middle🔥 161 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

INNER JOIN vs OUTER JOIN в SQL

Это один из самых важных концептов в SQL для объединения данных из нескольких таблиц. Разница в том, какие строки включаются в результат.

INNER JOIN — только совпадающие записи

Определение: Возвращает только строки, которые имеют совпадения в обеих таблицах.

Синтаксис:

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

Пример данных:

Таблица users:

id | name
---+-------
1  | Alice
2  | Bob
3  | Charlie

Таблица orders:

order_id | user_id | amount
---------+---------+-------
A1       | 1       | 100
A2       | 2       | 200
A3       | 4       | 300

Результат INNER JOIN:

id | name  | order_id
---+-------+----------
1  | Alice | A1
2  | Bob   | A2

Замечение: Charlie (id=3) исключён, потому что у неё нет заказов. Заказ A3 исключён, потому что user_id=4 не существует.

LEFT OUTER JOIN (LEFT JOIN)

Определение: Возвращает ВСЕ строки из левой таблицы + совпадающие строки из правой. Если совпадения нет → NULL.

Синтаксис:

SELECT u.id, u.name, o.order_id
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;

Результат LEFT JOIN на тех же данных:

id | name    | order_id
---+---------+----------
1  | Alice   | A1
2  | Bob     | A2
3  | Charlie | NULL

Charlieстанет в результат, но order_id будет NULL (потому что у неё нет заказов).

RIGHT OUTER JOIN (RIGHT JOIN)

Определение: Противоположность LEFT JOIN. Возвращает ВСЕ строки из правой таблицы + совпадающие строки из левой.

Синтаксис:

SELECT u.id, u.name, o.order_id
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;

Результат RIGHT JOIN:

id   | name  | order_id
-----+-------+----------
1    | Alice | A1
2    | Bob   | A2
NULL | NULL  | A3

Заказ A3 (user_id=4) включится в результат, но user.id и user.name будут NULL.

FULL OUTER JOIN (FULL JOIN)

Определение: Возвращает ВСЕ строки из обеих таблиц. Если совпадения нет → NULL с одной или с другой стороны.

Синтаксис:

SELECT u.id, u.name, o.order_id
FROM users u
FULL OUTER JOIN orders o ON u.id = o.user_id;

Результат FULL OUTER JOIN:

id   | name    | order_id
-----+---------+----------
1    | Alice   | A1
2    | Bob     | A2
3    | Charlie | NULL
NULL | NULL    | A3

Все строки из обеих таблиц присутствуют.

Визуальное представление

INNER JOIN — пересечение (∩)
┌─────────────┬─────────────┐
│   users     │   orders    │
│  ┌─────────┐┌─────────┐   │
│  │ совпадает        │  ← только эти
│  └─────────┘└─────────┘   │
└─────────────┴─────────────┘

LEFT JOIN — левая таблица полностью + совпадения
┌─────────────┬─────────────┐
│   users     │   orders    │
│  ┌───────────────────┐    │
│  │ ВСЕ users +       │ ←  совпадения
│  │ совпадающие       │
│  │ orders            │
│  └───────────────────┘    │
└─────────────┴─────────────┘

FULL OUTER JOIN — обе таблицы полностью
┌─────────────┬─────────────┐
│   users     │   orders    │
│  ┌───────────────────┐    │
│  │ ВСЕ строки из     │ ←  обеих таблиц
│  │ обеих таблиц      │
│  └───────────────────┘    │
└─────────────┴─────────────┘

Практические примеры

Пример 1: Найти всех пользователей И их заказы (если есть)

SELECT u.name, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name;

-- Результат:
-- Alice: 1 заказ
-- Bob: 1 заказ
-- Charlie: 0 заказов (NULL)

Пример 2: Найти несопоставленные заказы (где user не существует)

SELECT o.order_id, o.user_id
FROM orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE u.id IS NULL; -- только те, где совпадения не было

-- Результат:
-- A3 (user_id=4 не существует)

Пример 3: Найти пользователей БЕЗ заказов

SELECT u.id, u.name
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.id IS NULL; -- совпадения не было

-- Результат:
-- Charlie (id=3)

Пример 4: Все данные (полная таблица результатов)

SELECT u.name, o.order_id
FROM users u
FULL OUTER JOIN orders o ON u.id = o.user_id
ORDER BY u.id, o.order_id;

-- Alice A1
-- Bob A2
-- Charlie (NULL order_id)
-- (NULL user_id) A3

Когда использовать какой JOIN

INNER JOIN:

  • Когда нужны ТОЛЬКО совпадающие данные
  • Пример: найти пользователей, которые сделали заказ
SELECT u.name FROM users u
INNER JOIN orders o ON u.id = o.user_id;

LEFT JOIN:

  • Когда нужна левая таблица целиком + доп. данные из правой
  • Пример: все пользователи и их заказы (если есть)
SELECT u.name, COUNT(o.id) FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id;

RIGHT JOIN:

  • Редко используется (можно переписать как LEFT)
  • Когда правая таблица приоритетная
SELECT o.order_id, u.name FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
-- То же самое что LEFT JOIN с переставленными таблицами

FULL OUTER JOIN:

  • Когда нужны данные из обеих таблиц
  • Пример: сравнение двух наборов данных
SELECT * FROM users u
FULL OUTER JOIN orders o ON u.id = o.user_id;

Производительность

INNER JOIN — самый быстрый (фильтрует на лету)
LEFT/RIGHT JOIN — медленнее (хранит все строки левой/правой)
FULL OUTER JOIN — самый медленный (хранит все строки обеих)

Типичные ошибки

Ошибка 1: Использовать INNER вместо LEFT

-- Плохо: потеряем пользователей без заказов
SELECT u.name, o.order_id
FROM users u
INNER JOIN orders o ON u.id = o.user_id;

-- Хорошо:
SELECT u.name, o.order_id
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;

Ошибка 2: Забыть ON условие

-- Это вернёт декартово произведение (все комбинации)
SELECT * FROM users, orders;

-- Правильно с ON
SELECT * FROM users u
INNER JOIN orders o ON u.id = o.user_id;

В Node.js с ORM

TypeORM пример:

// INNER JOIN (по умолчанию)
const users = await userRepository
  .createQueryBuilder('u')
  .innerJoinAndSelect('u.orders', 'o')
  .getMany();

// LEFT JOIN
const users = await userRepository
  .createQueryBuilder('u')
  .leftJoinAndSelect('u.orders', 'o')
  .getMany();

Sequelize пример:

// INNER JOIN
const users = await User.findAll({
  include: { association: 'orders', required: true }
});

// LEFT JOIN (включить даже если нет заказов)
const users = await User.findAll({
  include: { association: 'orders', required: false }
});

Итог

JOINРезультат
INNERТолько совпадающие
LEFTВсе левой + совпадения
RIGHTВсе правой + совпадения
FULLВсе из обеих таблиц

Выбор JOIN зависит от того, какие данные нужны в результате. INNER — самый быстрый, но теряет данные. LEFT — самый популярный для сохранения полноты.

В чем разница между INNER JOIN и OUTER JOIN? | PrepBro