Какое минимальное и максимальное количество строк вернет запрос SELECT * FROM A LEFT JOIN B
Что проверяют
Понимаете ли вы, что LEFT JOIN не просто «все строки из A плюс совпадения», а может размножать строки. Следом почти всегда спрашивают, откуда в выборке из JPA вдруг появились дубли сущностей.
Как отвечать
Пусть в A n строк, в B m строк.
- минимум n: каждая строка
Aпопадает в результат хотя бы один раз, либо с найденными парами, либо одной строкой сNULLво всех колонкахB. ЕслиAпуста, результат тоже пустой; - максимум n × m: каждая строка
Aсовпала с каждой строкойB, например у всех строк одинаковый ключ или условие выродилось вON 1 = 1. ЕслиBпуста, результат всё равно n, а не ноль, поэтому точнее записатьn × max(m, 1).
Сколько получится на самом деле, определяет кратность связи. При связи «многие к одному» по первичному ключу B (заказ к клиенту) строк ровно n. При связи «один ко многим» результат растёт на число дочерних записей.
Для Java-разработчика это видно в Hibernate. JOIN FETCH по коллекции @OneToMany на уровне SQL возвращает строку на каждый дочерний элемент. В Hibernate 6 дубли корневых сущностей убираются автоматически, в 5.x для этого писали DISTINCT. Два fetch-джойна по двум List сразу дают декартово произведение и MultipleBagFetchException, а пагинация вместе с fetch-джойном коллекции выполняется в памяти, с предупреждением в логе.
Где спотыкаются
Отвечают, что максимум равен n, потому что «LEFT JOIN возвращает строки левой таблицы». Он возвращает их не меньше n, а насколько больше, зависит от числа совпадений.
Кладут условие на B в WHERE, а не в ON. Тогда строки с NULL отфильтровываются, LEFT JOIN фактически превращается в INNER JOIN, и минимум уже не n, а ноль.
Забывают про NULL в ключе. Сравнение NULL = NULL не даёт истину, поэтому такие строки A не совпадут ни с чем и придут с пустой правой частью.