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

В чем разница между типами JOIN?

1.0 Junior🔥 231 комментариев
#Базы данных и SQL

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

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

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

Разница между типами JOIN

Основной концепт

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

INNER JOIN

Возвращает только строки, где есть совпадение в обеих таблицах:

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

Пример:

Users:          Orders:
id | name       user_id | total
1  | John       1       | 100
2  | Jane       1       | 200
3  | Bob        4       | 150

INNER JOIN результат:
name | total
John | 100
John | 200
Jane (id=2) не появляется, потому что у неё нет заказов. Bob (id=3) не появляется, так как его id не совпадает с user_id.

// В Node.js с базой данных
const orders = await db.query(`
  SELECT u.name, o.total
  FROM users u
  INNER JOIN orders o ON u.id = o.user_id
`);

LEFT JOIN (LEFT OUTER JOIN)

Возвращает все строки из левой таблицы + соответствующие строки из правой. Если совпадения нет, правая часть будет NULL:

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

Пример:

Users:          Orders:
id | name       user_id | total
1  | John       1       | 100
2  | Jane       1       | 200
3  | Bob        4       | 150

LEFT JOIN результат:
name | total
John | 100
John | 200
Jane | NULL
Bob  | NULL

Все пользователи включены, даже если у них нет заказов.

RIGHT JOIN (RIGHT OUTER JOIN)

Возвращает все строки из правой таблицы + соответствующие строки из левой:

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

Пример:

RIGHT JOIN результат:
name | total
John | 100
John | 200
NULL | 150  -- заказ от user_id=4, который не существует

FULL OUTER JOIN

Возвращает все строки из обеих таблиц. Несовпадающие строки имеют NULL:

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

Пример:

FULL OUTER JOIN результат:
name | total
John | 100
John | 200
Jane | NULL
Bob  | NULL
NULL | 150  -- заказ с несуществующим пользователем

Примечание: SQL Server и PostgreSQL поддерживают FULL OUTER JOIN, но MySQL — нет. Для MySQL используй UNION:

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

CROSS JOIN

Декартово произведение — каждая строка из левой таблицы объединяется с каждой строкой из правой:

SELECT u.name, p.product_name
FROM users u
CROSS JOIN products p;

Пример:

Users: 3 строки (John, Jane, Bob)
Products: 2 строки (Laptop, Phone)

CROSS JOIN результат: 6 строк
name | product_name
John | Laptop
John | Phone
Jane | Laptop
Jane | Phone
Bob  | Laptop
Bob  | Phone

SELF JOIN

Объединение таблицы с самой собой:

SELECT e1.name as employee, e2.name as manager
FROM employees e1
JOIN employees e2 ON e1.manager_id = e2.id;

Пример: иерархия сотрудников

employees:
id | name  | manager_id
1  | Alice | NULL
2  | Bob   | 1
3  | Carol | 1
4  | David | 2

SELF JOIN результат:
employee | manager
Bob      | Alice
Carol    | Alice
David    | Bob

Практические примеры для Node.js приложения

Получить пользователей со всеми их заказами:

const userOrders = await db.query(`
  SELECT 
    u.id,
    u.name,
    u.email,
    o.id as order_id,
    o.total,
    o.created_at
  FROM users u
  LEFT JOIN orders o ON u.id = o.user_id
  ORDER BY u.id, o.created_at DESC
`);

// Результат:
// id | name | email           | order_id | total | created_at
// 1  | John | john@ex.com     | 1        | 100   | 2024-01-01
// 1  | John | john@ex.com     | 2        | 200   | 2024-01-02
// 2  | Jane | jane@ex.com     | NULL     | NULL  | NULL
// 3  | Bob  | bob@ex.com      | 3        | 150   | 2024-01-03

Найти пользователей с их профилями и постами:

const usersWithContent = await db.query(`
  SELECT 
    u.name,
    p.bio,
    po.title,
    po.content
  FROM users u
  LEFT JOIN profiles p ON u.id = p.user_id
  LEFT JOIN posts po ON u.id = po.user_id
  ORDER BY u.id
`);

Найти заказы со всеми деталями (заказ, покупатель, товары):

const orderDetails = await db.query(`
  SELECT 
    o.id as order_id,
    u.name as customer,
    i.product_id,
    pr.name as product_name,
    i.quantity,
    i.price
  FROM orders o
  INNER JOIN users u ON o.user_id = u.id
  INNER JOIN order_items i ON o.id = i.order_id
  INNER JOIN products pr ON i.product_id = pr.id
  WHERE o.id = $1
`, [orderId]);

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

JOINПроизводительностьПримечание
INNERБыстроМеньше всего строк
LEFTМедленнееБольше строк (NULL)
RIGHTМедленнееНе используется часто
FULLМедленнее всегоВсе строки из обеих таблиц
CROSSОчень медленноДекартово произведение, избегай

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

INNER JOIN:

  • Нужны только совпадающие записи
  • Удалить заказы без пользователя или пользователей без заказов
  • По умолчанию, если не нужны несовпадающие

LEFT JOIN:

  • Все из левой таблицы
  • Опциональные связи (пользователь может не иметь профиля)
  • Поиск "нет связанных данных" (WHERE order_id IS NULL)

RIGHT JOIN:

  • Редко используется, замени на LEFT с перестановкой таблиц
  • Все из правой таблицы

FULL OUTER JOIN:

  • Нужны все данные из обеих таблиц
  • Поиск несоответствий между таблицами

CROSS JOIN:

  • Комбинации (меню + напитки)
  • Вычисление всех возможных пар
  • Обычно избегай, может быть медленно

Заключение

Выбор JOIN зависит от того, какие данные нужны:

  • INNER — только совпадающие
  • LEFT — все слева + совпадающие справа
  • RIGHT — все справа + совпадающие слева
  • FULL — все из обеих таблиц
  • CROSS — все комбинации