У меня есть следующая схема базы данных Mysql:
User(Id, FirstName, LastName, NickName, Etc.)
Request(SenderId, ReceiverId)
Friendship(Id1, Id2)
Я считаю дружбу ненаправленным отношением, а это значит, что для каждой дружбы я дважды вставляю ее в таблицу Дружба. (Дайте мне знать, если это не очень хорошая идея, пожалуйста).
То, что я пытаюсь получить, - это список пользователей, которые не являются друзьями определенного пользователя (позвольте мне назвать его UserX) и не имеют текущего запроса к/от него.
Мои первоначальные испытания привели меня к следующему:
SELECT User.Id, User.NickName, User.Picture FROM User
LEFT JOIN Friendship A ON User.Id = A.Id1
LEFT JOIN Friendship B ON User.Id = B.Id2
LEFT JOIN Request C ON User.Id = C.Sender
LEFT JOIN Request D ON User.Id = D.Reciever
WHERE User.Id <> ?
И, конечно же, заполнитель — это идентификатор пользователя X.
Это не работает, потому что, хотя кортежи, которые имеют дружеские отношения или запросы с UserX, удаляются, друзья по-прежнему появляются, потому что у них есть дружеские отношения с другими пользователями!
Заранее спасибо.
Используя левое соединение со списком объединения:
select *
from User u1
left join
(
select ID2 as id
from Friendships
where ID1 = 'UserX'
union all
select ID1
from Friendships
where ID2 = 'UserX'
union all
select Sender
from Request
where Receiver = 'UserX'
union all
select Receiver
from Request
where Sender = 'UserX'
) ux
on ux.id = u1.id
where ux.id is null
and ux.id <> 'UserX'
Работает прекрасно, кроме того, что возвращает еще и самого UserX. Спасибо.
Что, если вы соберете все отдельные идентификаторы из таблицы «запрос» и «дружба», а затем выберите записи из идентификатора пользователя, недоступные в приведенном выше списке.
SELECT Id, FirstName, LastName, NickName
FROM User
WHERE ID NOT IN
(
SELECT DSTINCT Id1 ID FROM Friendship
UNION
SELECT DSTINCT Id2 FROM Friendship
UNION
SELECT DSTINCT SenderId FROM Request
UNION
SELECT DSTINCT ReceiverId FROM Request
)A
Если вам нужно эффективное решение, используйте not exists
несколько раз:
select u.*
from user u
where not exists (select 1 from friendship f where f.id1 = u.id and f.id2 = ?) and
not exists (select 1 from friendship f where f.id2 = u.id and f.id1 = ?) and
not exists (select 1 from request r where r.SenderId = u.id and r.ReceiverId = ?) and
not exists (select 1 from request r where r.ReceiverId = u.id and r.SenderId = ?);
В частности, для этого можно использовать индексы:
friendship(id1, id2)
friendship(id2, id1)
request(SenderId, ReceiverId)
request(ReceiverId, SenderId)
Это должно иметь гораздо лучшую производительность, чем решения, которые union
подзапросы вместе.
Я боюсь, что это возвращает только пользователей, у которых вообще нет друзей.
@АхмедХаммад. . . Конечно, он должен проверить идентификатор, который вы хотите.
Предоставьте образцы данных и желаемые результаты.