То, что "с ифами" - это Nested Loops, вложенные циклы. В общем случае может применяться, если в условии join не простое равенство, какой nonequity предикат. В самой наивной реализации даёт квадратичную сложность (m*n), поэтому каждый оптимизатор стремится избавиться от этого соединения.
Объединение "через индекс" - это, как я понимаю Sort Merge Join, линейная сложность самого соединения (m+n), однако наборы данных нужно отсортировать, а это лог-линейная сложность. Всё лучше, чем квадратичная. Если данные отсортированы заранее, то это самый лучший вариант.
Или можно построить хеш-таблицу для меньшего (или менее распределенного, много тонкостей) отношения. Получится (Broadcast) Hash Join, данные можно не сортировать, тоже линейная сложность.
Хорошие оптимизаторы также сделают много, чтобы уменьшить соединяемые наборы данных, например, добавят Semi Join, которого не было в самом запросе. И даже динамические приёмчики могу пойти в ход, если полезная информация стала известной только во время выполнения запроса.
Но спарковский Catalyst основан на правилах (rule based optimizer). Недавно выясняли, уберёт он из датафрейма поля, если они не нужны, или будет таскать по всем шафлам и дропнет в последний момент. Я даже сам засомневался, а спарк убрал все ненужные поля. А это всего лишь отработала эвристика, нужная и хорошая, но не зависящая от самих данных.
Может что-то поменялось в третьем спарк, пока не могу сказать. Но пока приходится вводить дополнительные правила-эвристики, если существующие чем-то не устраивают, это может быть непросто и порождает код, техдолг и баги