Union двух датафреймов просто и эффективно выполняется, это дешёвая операция. Раньше (spark 2.2, может и более поздние версии, не следил) была проблема с построением плана, содержащего очень большое количество union (сотни, тысячи), потому что для каждого нового проверялись схемы всех ранее добавленных датафреймов, а это сложность O(n^2). В 2.4 проблему пофиксили, добавив "объединитель" юнионов в планах catalyst. Теперь разница очень невелика и можно ничем не заменять, только если очень хочется))
И ещё есть unionByName, можно не заботится о выборке полей в одинаковом порядке, спарк сам упорядочит как надо
Заменить можно через sparkContext.union(RDDs) и создание датафрейма на основе RDD и схемы, это всегда работает быстро и надёжно
Если есть навыки работы с планами Catalyst, то ещё лучше: можно передать в Union (это элемент логического плана) сколько угодно других логических планов. Самый быстрый известный мне способ
Или вот такой вариант: просто материализовать в одной и той же директории (сохранить) и в дальнейшем читать оттуда. Сохранение займёт время, будет оверхед на запуск джобов и их нельзя просто параллелить (одна и та же директория - возможны конфликты записи). Но иногда это как раз то, что нужно