Почему в одном случае используется join, а в другом нет?
Что проверяют
Понимаете ли вы, что соединение таблиц это следствие того, какие данные нужны в результате, а не вопрос вкуса. У Java-разработчика тема почти всегда уходит в JPA: видите ли вы разницу между join и join fetch и знаете ли цену соединения на связи один ко многим.
Как отвечать
Соединение нужно, когда в результат попадают колонки другой таблицы или когда по ней идёт фильтр, который иначе не выразить. Не нужно в трёх ситуациях:
- хватает внешнего ключа. Идентификатор клиента лежит в самой таблице заказов, присоединять таблицу клиентов ради него незачем. В JPA тот же смысл у
getReference: прокси без запроса, пока не тронули поля; - проверяется только наличие связанной строки. Тогда честнее
existsилиin: соединение на связи один ко многим размножит строки, и придётся чинить это черезdistinct, который добавит сортировку или хэширование; - нужны агрегаты по двум разным коллекциям. Два соединения перемножаются между собой, и суммы получаются завышенными, поэтому считают подзапросами или отдельными запросами.
В JPA различие стоит назвать явно: join o.items i делает связь доступной в условии, но коллекцию не грузит, и снаружи транзакции вы получите либо LazyInitializationException, либо N плюс один запрос. Грузит только join fetch:
select o from Order o left join fetch o.items where o.status = :statusОборотная сторона: join fetch сразу по двум коллекциям даёт декартово произведение строк, поэтому за один запрос фетчат одну коллекцию, а остальное добирают пакетной загрузкой.
Где спотыкаются
Пишут left join, а условие на правую таблицу кладут в where. Строки без пары отсеиваются, и внешнее соединение молча превращается во внутреннее. Условию место в on.
Ставят join fetch всюду, лишь бы не ловить N плюс один, и вытягивают в память половину таблицы. Решение зависит от кардинальности: для ссылки многие к одному фетч дёшев, для длинной коллекции дешевле отдельный запрос.
Считают, что подзапрос заведомо медленнее соединения. Планировщик обычно переписывает коррелированный exists в полусоединение, и по времени разницы нет, а вот соединение с distinct на связи один ко многим стоит дорого.