Может ли LEFT JOIN вернуть больше строк, чем в левой таблице?
Короткий ответ
Да, может. LEFT гарантирует, что каждая строка левой таблицы появится минимум один раз, но если в правой таблице по условию соединения ей соответствует несколько строк — строка размножится. Больше строк, чем в левой, получается при связи один-ко-многим. Меньше — не станет никогда.
Вопрос-фильтр на middle: junior обычно уверенно говорит «нет, LEFT сохраняет левую таблицу как есть». А правильный ответ — «да, может», и вот тут начинается интересное.
Как рассуждать
Разложи гарантию LEFT JOIN на две части. LEFT обещает, что каждая строка левой таблицы попадёт в результат хотя бы один раз. Он ничего не обещает про «ровно один раз».
Дальше всё решает кардинальность связи. Если условие соединения находит для строки слева несколько подходящих строк справа — эта строка продублируется по числу совпадений. Значит, итог зависит от того, один-к-одному соединение или один-ко-многим.
Проверь себя вопросом: «по какому полю соединяю и уникально ли оно в правой таблице?». Если поле справа не уникально — жди размножения.
Эталонный ответ
Да. Строк станет больше, если справа связь один-ко-многим. Классический пример — фильмы и их прокаты: у одного фильма (через инвентарь) десятки записей об аренде.
SELECT f.title, r.rental_date
FROM film f
LEFT JOIN inventory i ON i.film_id = f.film_id
LEFT JOIN rental r ON r.inventory_id = i.inventory_id;
В film — 1000 строк, а результат — около шестнадцати тысяч: каждый фильм повторяется столько раз, сколько его брали в прокат. Оба соединения здесь LEFT сознательно: сделай первое INNER — и фильмы, у которых вообще нет копий в inventory (в dvdrental таких 42), молча исчезнут ещё до второго джойна, никакой LEFT ниже по цепочке их уже не вернёт. С двумя LEFT в выводе остаются и фильмы без копий, и копии без единой аренды (с NULL в rental_date). А вот на размножение строк тип JOIN не влияет — за дубли отвечает не LEFT против INNER, а множественность совпадений справа.
Отсюда практический вывод: перед агрегацией после JOIN всегда думай, не раздул ли ты гранулярность. Если нужно посчитать выручку по фильму, а таблица размножилась — наивный count(*) посчитает строки-дубликаты, а не фильмы. Спасает либо агрегация в подзапросе до соединения, либо count(DISTINCT ...). Особенно коварны цепочки из нескольких JOIN: два соединения один-ко-многим подряд перемножают количество строк, и итог раздувается уже квадратично.
Сильный ответ добавляет: «меньше строк, чем в левой таблице, LEFT JOIN не вернёт никогда — это его инвариант. Только столько же или больше». И упоминает, что фильтр правой таблицы, случайно оказавшийся в WHERE, наоборот, может строк убрать — превратив LEFT в фактический INNER.
Частые ошибки
- Отвечают «нет» на автомате. Это выдаёт, что человек думает про LEFT как про «приклей столбцы», а не про соединение по условию.
- Не учитывают размножение перед
GROUP BYи получают завышенные суммы. Дубликаты строк ломают агрегаты — та же ловушка, что при поиске дубликатов. - Забывают, что
WHERE r.return_date IS NOT NULLпосле LEFT JOIN выкидывает строки без пары. Чтобы сохранить семантику LEFT, такие условия идут вON. Это же — источник сюрпризов с поведением NULL. - Путают направление: думают, что «размножает» левая таблица. Размножает именно множественность справа — обзор типов есть в разборе видов JOIN.
Потренируй этот вопрос с ИИ-интервьюером
Читать разбор полезно, а отвечать вслух под давлением — совсем другое. ИИ-интервьюер задаст этот и смежные вопросы, будет копать глубже и разберёт твои ответы. Первое короткое интервью — бесплатно.
Пройти собеседование бесплатно