Reputation: 2480
I have a table which represents relations between pairs of people.
user_relating | user_related | relation_type
--------------------------------------------
1 | 2 | 1 --> means user 1 follows user 2
I need to find all friends for a specific user X. Friendship means that two people follow each other. So if user A follows users B, C. And users B, C follow A, then B, C are friends of A.
I've written this query :
SELECT users.* -- user's followers
FROM users
JOIN user_relations rel
ON users.id = rel.user_relating AND user_related = 2
INTERSECT
SELECT users.* -- who the user follows
FROM users
JOIN user_relations rel
ON users.id = rel.user_related AND user_relating = 2;
But I think it's inefficient. Is there a more optimized way to get this done?
I've tried doing something like this :
SELECT DISTINCT f.*
FROM users f
JOIN user_relations u1
on f.id = u1.user_related
JOIN user_relations u2
on f.id = u2.user_relating
WHERE u1.user_related = 2
or u2.user_related = 2;
it seems much more efficient judging by the EXPLAIN ANALYZE (though I only have a really small table, like 10 rows so I'm not sure it's a good measurement).
But the problem here is that it returns the user in question too. Meaning, if I want friends of User B , then this query returns User B together with all his friends. Can I somehow exclude User B from the query result?
And, as stated formerly, I would be glad to receive some ideas for the most optimized and efficient way to do this kind of queries.
Upvotes: 0
Views: 69
Reputation: 1270021
Probably the most efficient method is:
select ur.*
from user_relations ur
where ur.user_relating < ur.user_related and
ur.relation_type = 1 and
exists (select 1
from user_relations ur2
where ur2.user_relating = ur.user_related and
ur2.user_related = ur.user_relating and
ur2.relation_type = 1
);
And for performance, you want an index on user_relations(user_relating, user_related, relationship_type)
.
That said, this is similar to your version with the join
, but does not require removing duplicates.
If you have lots of different relationship types, then an index on that column could also help.
EDIT:
If you have a particular user and want their friends:
select (case when user_relating = 2 then user_related else user_relating end) as user_friend
from user_relations ur
where ur.user_relating < ur.user_related and
ur.relation_type = 1 and
exists (select 1
from user_relations ur2
where ur2.user_relating = ur.user_related and
ur2.user_related = ur.user_relating and
ur2.relation_type = 1
) and
2 in (ur.user_relating, user_related)
Upvotes: 1