Контекст отсутствует, вопрос слишком изолирован, и ответы очень ограничены. Тема не завершена.
Во-первых, для понимания представленная таблица друзей не существует сама по себе. Это дочерний элемент таблицы User с PK UserId. И UserId, и FriendId являются FK для UserId.
Поскольку дружба - это улица с двусторонним движением или двунаправленная (здесь мы исключаем невзаимных любовников и сталкеров!), Тогда в одной строке указывается отношение в обоих направлениях.
Существуют серьезные последствия в именовании любого столбца, поэтому я бы избегал (UserId, FriendId), который подразумевает иерархию или одностороннее отношение, которое является ложным. Я предлагаю (Friend1, Friend2), и, конечно, вам нужен PK, чтобы быть ((Friend1, Friend2).
То, что не было идентифицировано, так это то, что мы до сих пор не предотвратили дубликаты ([5,12] и [12, 5]), та самая избыточность, которую вы хотите предотвратить. Нам нужно правило для обеспечения порядка: Friend1 всегда меньше Friend2.
Существуют и другие отношения, такие как ведомость материалов или родословная, в которой есть сборки и компоненты, для которых требуется нечто большее. Например, (AssemblyId, ComponentId) или (ParentID, ChildId), где это правило не применяется: деталь может быть компонентом в сборке, а также самой сборкой. Это требует двух рядов. Все еще нет дубликатов.