$ sudo teach|
    $ sudo teach|
    IT school
  • Telegram
  • Партнёрам
  • Все курсы
$ sudo teach IT
OOO "SALEPROFIT"Контакты и реквизитыIT-Park Logo

Школа

  • Блог
  • Проверить сертификат

Сотрудничество

  • Стать учителем
  • Партнёрская программа
  • О проекте

Право

  • Оферта
  • Политика конфиденциальности

© 2023–2026 $ sudo teach IT™. All Rights Reserved. Public user contributions licensed under CC BY-SA 4.0 license with attribution required
TelegramGitHubYouTube
ГлавнаяБлогSQL JOIN за 10 минут: наглядный гид с реальными примерами

SQL JOIN за 10 минут: наглядный гид с реальными примерами

$ sudo teach IT
·16 июля 2026 г.·9 мин·68
SQL JOIN за 10 минут: наглядный гид с реальными примерами

Объясняем INNER, LEFT, RIGHT и FULL JOIN на живых таблицах — без воды и абстрактных схем, только запросы и результаты.

Содержание
Данные, с которыми мы будем работатьINNER JOIN: только совпаденияLEFT JOIN: все из левой таблицыRIGHT JOIN: все из правой таблицыFULL JOIN: все строки из обеих таблицАгрегация поверх JOIN: реальный рабочий запросЧастые ошибки при работе с JOINДекартово произведение вместо JOINНеоднозначные имена столбцовJOIN по неиндексированным столбцамБыстрая шпаргалкаИтог

Большинство проблем с JOIN возникают не потому, что концепция сложная. Она действительно простая. Проблемы возникают потому, что её объясняют через абстрактные диаграммы Венна, которые красиво выглядят на слайдах, но не помогают понять, что происходит с реальными строками в реальных таблицах.

В этом материале мы разберём все четыре основных типа JOIN — INNER, LEFT, RIGHT и FULL — на одном наборе данных. Вы увидите конкретные запросы и конкретные результаты. К концу статьи у вас будет рабочая модель в голове, а не просто запомненные термины.


Данные, с которыми мы будем работать

Возьмём классический, но реалистичный сценарий: интернет-магазин. У нас есть две таблицы — customers (покупатели) и orders (заказы).

Таблица customers:

customer_idnamecity
1Анна СмирноваМосква
2Иван ПетровСанкт-Петербург
3Мария КозловаКазань
4Дмитрий ОрловНовосибирск

Таблица orders:

order_idcustomer_idamountproduct
10114500.00Ноутбук
10221200.00Мышь
1031890.00Клавиатура
10453200.00Монитор

Обратите внимание на намеренно созданные «несоответствия»: у Марии Козловой и Дмитрия Орлова нет заказов. Заказ с customer_id = 5 ссылается на несуществующего покупателя. Именно эти случаи и делают выбор типа JOIN принципиальным.

Создадим таблицы и заполним их:

code
CREATE TABLE customers (
    customer_id INT PRIMARY KEY,
    name        VARCHAR(100),
    city        VARCHAR(100)
);

CREATE TABLE orders (
    order_id    INT PRIMARY KEY,
    customer_id INT,
    amount      DECIMAL(10, 2),
    product     VARCHAR(100)
);

INSERT INTO customers VALUES
    (1, 'Анна Смирнова',  'Москва'),
    (2, 'Иван Петров',    'Санкт-Петербург'),
    (3, 'Мария Козлова',  'Казань'),
    (4, 'Дмитрий Орлов',  'Новосибирск');

INSERT INTO orders VALUES
    (101, 1, 4500.00, 'Ноутбук'),
    (102, 2, 1200.00, 'Мышь'),
    (103, 1,  890.00, 'Клавиатура'),
    (104, 5, 3200.00, 'Монитор');

INNER JOIN: только совпадения

INNER JOIN — самый распространённый тип. Он возвращает строки, для которых нашлось совпадение в обеих таблицах по условию соединения.

code
SELECT
    c.customer_id,
    c.name,
    o.order_id,
    o.product,
    o.amount
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id;

Результат:

customer_idnameorder_idproductamount
1Анна Смирнова101Ноутбук4500.00
1Анна Смирнова103Клавиатура890.00
2Иван Петров102Мышь1200.00

Что произошло: Мария Козлова и Дмитрий Орлов выпали — у них нет заказов. Заказ 104 тоже выпал — у него нет соответствующего покупателя. В результат попали только те строки, где customer_id нашёлся в обеих таблицах одновременно.

Когда использовать: когда вас интересуют только «полные» данные. Например, отчёт о продажах, где нужны имена покупателей и суммы их заказов — без пустых строк.

Типичная ошибка: использовать INNER JOIN там, где нужен LEFT JOIN, и потом удивляться, почему часть данных «пропала». Если вы делаете аналитику и хотите видеть всех покупателей, включая тех, кто ничего не купил, — INNER JOIN вас подведёт.


LEFT JOIN: все из левой таблицы

LEFT JOIN (или LEFT OUTER JOIN — это синонимы) возвращает все строки из левой таблицы и совпадающие строки из правой. Если совпадения нет — правая часть заполняется NULL.

code
SELECT
    c.customer_id,
    c.name,
    o.order_id,
    o.product,
    o.amount
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id;

Результат:

customer_idnameorder_idproductamount
1Анна Смирнова101Ноутбук4500.00
1Анна Смирнова103Клавиатура890.00
2Иван Петров102Мышь1200.00
3Мария КозловаNULLNULLNULL
4Дмитрий ОрловNULLNULLNULL

Теперь все покупатели в результате. Мария и Дмитрий есть, но их столбцы из таблицы orders — пустые. Заказ 104 по-прежнему отсутствует, потому что его customer_id = 5 не существует в левой таблице.

Практический сценарий — найти покупателей без заказов:

code
SELECT
    c.customer_id,
    c.name
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
WHERE o.order_id IS NULL;

Результат:

customer_idname
3Мария Козлова
4Дмитрий Орлов

Это классический паттерн: LEFT JOIN + фильтр по NULL в правой таблице. Он работает быстрее и читается чище, чем вложенный NOT IN или NOT EXISTS в большинстве случаев.

Когда использовать: практически всегда, когда вы хотите сохранить все записи из основной таблицы. Аналитика активности пользователей, отчёты с нулевыми значениями, поиск «сирот» в данных — всё это LEFT JOIN.


RIGHT JOIN: все из правой таблицы

RIGHT JOIN — зеркальное отражение LEFT JOIN. Возвращает все строки из правой таблицы и совпадающие из левой.

code
SELECT
    c.customer_id,
    c.name,
    o.order_id,
    o.product,
    o.amount
FROM customers c
RIGHT JOIN orders o ON c.customer_id = o.customer_id;

Результат:

customer_idnameorder_idproductamount
1Анна Смирнова101Ноутбук4500.00
2Иван Петров102Мышь1200.00
1Анна Смирнова103Клавиатура890.00
NULLNULL104Монитор3200.00

Теперь все заказы в результате, включая заказ 104 с несуществующим покупателем. Мария и Дмитрий снова выпали — у них нет заказов в правой таблице.

Честный комментарий: RIGHT JOIN используется редко. Большинство разработчиков просто меняют порядок таблиц и пишут LEFT JOIN — это читается естественнее. Следующие два запроса дают идентичный результат:

code
-- Вариант с RIGHT JOIN
SELECT * FROM customers c
RIGHT JOIN orders o ON c.customer_id = o.customer_id;

-- Эквивалентный вариант с LEFT JOIN (более распространённый стиль)
SELECT * FROM orders o
LEFT JOIN customers c ON c.customer_id = o.customer_id;

Если в вашей команде нет жёсткого соглашения по стилю — придерживайтесь LEFT JOIN. Код будет однороднее и понятнее при чтении.


FULL JOIN: все строки из обеих таблиц

FULL JOIN (или FULL OUTER JOIN) возвращает все строки из обеих таблиц. Где совпадений нет — ставит NULL с соответствующей стороны.

code
SELECT
    c.customer_id   AS c_id,
    c.name,
    o.order_id,
    o.customer_id   AS o_cid,
    o.product,
    o.amount
FROM customers c
FULL JOIN orders o ON c.customer_id = o.customer_id;

Результат:

c_idnameorder_ido_cidproductamount
1Анна Смирнова1011Ноутбук4500.00
1Анна Смирнова1031Клавиатура890.00
2Иван Петров1022Мышь1200.00
3Мария КозловаNULLNULLNULLNULL
4Дмитрий ОрловNULLNULLNULLNULL
NULLNULL1045Монитор3200.00

Теперь видно всё: и покупатели без заказов, и заказ без покупателя. Это максимально полная картина состояния данных.

Важно: FULL JOIN поддерживается не во всех СУБД. MySQL, например, его не поддерживает напрямую. Там используют эмуляцию через UNION:

code
-- Эмуляция FULL JOIN в MySQL
SELECT c.customer_id, c.name, o.order_id, o.product, o.amount
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id

UNION

SELECT c.customer_id, c.name, o.order_id, o.product, o.amount
FROM customers c
RIGHT JOIN orders o ON c.customer_id = o.customer_id
WHERE c.customer_id IS NULL;

PostgreSQL, SQL Server и Oracle поддерживают FULL JOIN нативно.

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


Агрегация поверх JOIN: реальный рабочий запрос

Понимание JOIN по-настоящему проверяется, когда вы начинаете комбинировать их с агрегатными функциями. Вот типичная задача: получить список всех покупателей с суммой их заказов, включая тех, кто ещё ничего не купил.

code
SELECT
    c.customer_id,
    c.name,
    c.city,
    COUNT(o.order_id)       AS order_count,
    COALESCE(SUM(o.amount), 0) AS total_spent
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.name, c.city
ORDER BY total_spent DESC;

Результат:

customer_idnamecityorder_counttotal_spent
1Анна СмирноваМосква25390.00
2Иван ПетровСанкт-Петербург11200.00
3Мария КозловаКазань00.00
4Дмитрий ОрловНовосибирск00.00

Два момента, на которые стоит обратить внимание:

  1. COUNT(o.order_id) считает только не-NULL значения, поэтому для покупателей без заказов возвращает 0, а не 1.
  2. COALESCE(SUM(o.amount), 0) заменяет NULL на 0. Без него покупатели без заказов показали бы NULL в колонке total_spent, что некорректно для финансового отчёта.

Если вы только начинаете систематически разбираться с SQL — от базового синтаксиса до таких запросов — стоит посмотреть на структурированный курс. Например, «SQL – для начинающих!» от sudo teach IT строится именно на практике с реальными данными, а не на теоретических определениях.


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

Декартово произведение вместо JOIN

Если забыть условие ON или написать его неверно, получите декартово произведение — каждая строка левой таблицы соединится с каждой строкой правой. При 1000 строк в каждой таблице результат будет содержать миллион строк.

code
-- Опасный запрос — отсутствует условие соединения
SELECT * FROM customers, orders;
-- Вернёт 4 × 4 = 16 строк вместо ожидаемых

-- Правильно
SELECT * FROM customers c
JOIN orders o ON c.customer_id = o.customer_id;

Неоднозначные имена столбцов

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

code
-- Плохо: неясно, из какой таблицы customer_id
SELECT customer_id, name, amount
FROM customers
JOIN orders ON customers.customer_id = orders.customer_id;

-- Хорошо: явные псевдонимы таблиц
SELECT c.customer_id, c.name, o.amount
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id;

JOIN по неиндексированным столбцам

Технически запрос работает, но на больших таблицах производительность падает катастрофически. Столбцы, по которым вы делаете JOIN, должны быть проиндексированы — как правило, это первичные и внешние ключи.

code
-- Убедитесь, что индекс на внешнем ключе существует
CREATE INDEX idx_orders_customer_id ON orders(customer_id);

Быстрая шпаргалка

Тип JOINЧто возвращает
INNER JOINТолько строки с совпадениями в обеих таблицах
LEFT JOINВсе строки из левой + совпадения из правой (NULL если нет)
RIGHT JOINВсе строки из правой + совпадения из левой (NULL если нет)
FULL JOINВсе строки из обеих таблиц (NULL там, где нет совпадения)

Итог

JOIN — это не синтаксис, который нужно зазубрить. Это инструмент, который нужно понять через данные. Задайте себе один вопрос перед написанием запроса: «Что должно произойти со строками, у которых нет пары?» Если они должны исчезнуть — INNER JOIN. Если остаться — LEFT или FULL JOIN в зависимости от того, строки из какой таблицы важнее.

Практика на реальных данных быстро закрепляет эту логику. Создайте свои тестовые таблицы, намеренно добавьте несоответствия — как мы сделали с заказом 104 — и посмотрите, как ведут себя разные типы JOIN. Это работает лучше любых диаграмм.

Если хотите пройти путь от основ до уверенной работы с запросами системно, курс «SQL – для начинающих!» — один из разумных способов структурировать это обучение с практическими заданиями на каждом шаге.

Войдите, чтобы поставить лайк и оставить комментарий.

Автор

$ sudo teach IT

SQL – для начинающих!

Курс по теме

SQL – для начинающих!

С нуля до уверенных запросов на SQL. Никакой теории без дела — только живые запросы, настоящие данные и готовность к работе с любой базой. Хватит гуглить «как написать JOIN» — разберись один раз и забудь.

Бесплатно
Открыть курс

Продолжите обучение

Все курсы
Python – для начинающих!

Python – для начинающих!

С нуля до профессионального уровня. Подходит для всех. Учитесь каждый день и овладейте самым популярным языком программирования.

Перейти к курсу
Приложения для iPhone и Apple Watch на SwiftUI

Приложения для iPhone и Apple Watch на SwiftUI

Разработка приложений для iPhone и Apple Watch на SwiftUI: навигация, SwiftData, виджеты, часы, выпуск. Нужен Mac с Xcode 27, сами устройства не нужны.

Перейти к курсу
Ботостроение Telegram

Ботостроение Telegram

Лёгкий, быстрый и доступный способ познакомиться с миром ботостроения в Telegram. Видео, конспекты, практика и помощь – всё у нас на курсе.

Перейти к курсу
Приложения для macOS на SwiftUI

Приложения для macOS на SwiftUI

Разработка приложений для Mac на SwiftUI: окна и меню, Liquid Glass, SwiftData, сеть, выпуск. Нужен Mac с macOS 27 и Xcode 27.

Перейти к курсу

Другие статьи

SQL: диаграмма жизненного цикла индекса — от EXPLAIN до теста на регрессию производительности
диаграмма

SQL: диаграмма жизненного цикла индекса — от EXPLAIN до теста на регрессию производительности

Покажем практический процесс: как определить, какой индекс реально используется, когда он бесполезен, как проверить влияние после изменений схемы и как не словить деградацию запросов в будущем.

16 сентября 2026 г.
1050
Как писать эффективные SQL-запросы под доменную модель: меньше JOIN’ов, больше ясности
писать

Как писать эффективные SQL-запросы под доменную модель: меньше JOIN’ов, больше ясности

Разберём технику проектирования запросов от сущностей к данным: фильтрация, агрегации, ограничение полей и предсказуемые результаты. Покажем, как избегать дублирования строк и “случайных” декартовых произведений.

25 июля 2026 г.
630
Создание Telegram бота в 2026 легко и просто! Полный курсы!
создание

Создание Telegram бота в 2026 легко и просто! Полный курсы!

19 июня 2026 г.
1421
Как выбрать, куда идти в IT: веб, боты, данные — без хаоса
выбрать

Как выбрать, куда идти в IT: веб, боты, данные — без хаоса

Разберём самые понятные направления для новичка (веб, боты, аналитика/данные) и чем они отличаются по шагам обучения. В конце вы получите простой план, как проверить, что вам подходит, и какой стартовый путь выбрать.

5 октября 2026 г.
580
Путь от README до демо: как собрать портфолио разработчика, чтобы его хотели смотреть
путь

Путь от README до демо: как собрать портфолио разработчика, чтобы его хотели смотреть

Разберём структуру репозитория, как упаковать проект в понятную историю (проблема → решение → запуск → метрики качества) и какие артефакты повышают доверие: скринкасты, архитектурная схема, чек-лист продакшн-готовности.

17 сентября 2026 г.
1070
Как понять, какой язык программирования учить первым: простой тест и выбор
понять

Как понять, какой язык программирования учить первым: простой тест и выбор

Помогу новичку выбрать первый язык без стресса: объясню варианты по целям (веб, боты, анализ данных), что учить в первую очередь и как не потратить месяцы впустую. В конце — краткий план старта на ближайшие недели.

2 октября 2026 г.
620

Комментарии

Станьте первым в обсуждении

Поделитесь мнением о статье или задайте вопрос автору

Содержание

Данные, с которыми мы будем работатьINNER JOIN: только совпаденияLEFT JOIN: все из левой таблицыRIGHT JOIN: все из правой таблицыFULL JOIN: все строки из обеих таблицАгрегация поверх JOIN: реальный рабочий запросЧастые ошибки при работе с JOINДекартово произведение вместо JOINНеоднозначные имена столбцовJOIN по неиндексированным столбцамБыстрая шпаргалкаИтог