Конечно, дублирование информации в бд - не нормальный вариант. Но рекурсивный проход для SQL систем - тоже ненормальный вариант. Об этом на википедии даже статья есть:
https://en.wikipedia.org/wiki/Hierarchical_and_rec...
Я решаю такую задачу одним из двух вариантов, как и Вы.
В первом варианте - это джойн самой же таблицы по вторичному ключу. Таким образом каждая запись имеет в результирующей таблице своего предка, что удобно для последующей неSQL обработки.
Во втором варианте я нарушаю нормальную форму созданием линковочной таблицы, обновление которой вешаю тригером на древовидную таблицу, которую обрабатываю. Так при небольшой избыточности можно получить большой прирост производительности.
Другие варианты:
Еще я реализовывал рекурентный обход через хранимые функции, - это медленный вариант.
При разработке соцсети, я бы использовал второй вариант. Рекомендую реализовать оба и провести нагрузочное тестирование при больших объемах. А дальше выбрать на основе анализа данных после тестирования.