2011-04-26 111 views
6

我正在构建一个应用程序,用户可以相互连接(就像任何社交网络中的朋友一样)。“朋友的朋友”喜欢sql查询

我保存在一个表具有以下结构的连接:

id_user1 | id_user2 | is_accepted | is_blocked | created_at 

用户之间的连接是双向的,因此当两个用户连接只有一个表中的记录。如果user_idid_user1id_user2列表中,则无关紧要。

现在我需要编写一个sql查询来获取某些用户的“朋友的朋友”,这些用户并不是用户的朋友。 此外,用户必须被接受并且不被阻止。

恢复中,这里是我需要执行的步骤。

  1. 找到与我希望用户(id_user1 = current_userid_user2 = current_useris_accepted!blocked

  2. 的foreach返回user_ids的相关联的所有用户ID - >获取所有相关用户(忽略与当前协会用户)(确保它也是accepted!blocked)。

我该怎么做这样的查询?

感谢您的帮助。

+2

我不认为你真的希望这张桌子是双向的。如果A阻止B但B仍然想从A听到?更不用说,如果你这样做,那么做自我加入会容易得多。 – drysdam 2011-04-26 16:14:50

+0

我想如果A阻止B,不仅他不想听到B,而且也不想让B听到他的消息。 – Andre 2011-04-26 16:19:13

+0

如果你想要达到一个级别,那么自我加入会做...如果你想多走一个级别,那么我想层次查询更好,但我不知道如何在MySQL中实现。 – 2011-04-26 16:22:13

回答

2
SELECT CASE f2.id_user1 WHEN CASE f1.id_user1 WHEN $user THEN f1.id_user2 ELSE f1.id_user1 END THEN f2.id_user2 ELSE f2.id_user1 END 
FROM friends f1 
JOIN friends f2 
ON  f2.id_user1 = CASE f1.id_user1 WHEN $user THEN f1.id_user2 ELSE f1.id_user1 END 
     OR f2.id_user2 = CASE f1.id_user1 WHEN $user THEN f1.id_user2 ELSE f1.id_user1 END 
WHERE (f1.id_user1 = $user OR f1.id_user = $user) 
     AND f1.is_accepted = 1 
     AND f2.is_accepted = 1 
     AND f1.is_blocked = 0 
     AND f2.is_blocked = 0 
     AND NOT (f1.id_user1, f1.id_user2) = (f2.id_user1, f2.id_user2) 

请注意,最好先存储用户,最好是第二位。在这种情况下,查询会更简单。

+0

我已经测试过它,它的工作原理。谢谢 – brpaz 2011-04-27 08:14:18

1

当直接使用每条记录的one-record-table时,所有查询都会变得臃肿且容易出错,因为您经常需要编写'id_user1 = ...或id_user2 = ...'。我会创建一个视图

CREATE VIEW bidifreinds (id_user1, id_user2, is_accepted, is_blocked, created_at) AS 
SELECT id_user1, id_user2, is_accepted, is_blocked, created_at FROM friends 
UNION 
SELECT id_user2, id_user1, is_accepted, is_blocked, created_at FROM friends 

这会让生活变得更容易。

然后,你可以写

SELECT f1.id_user1, f2.id_user2 
FROM friends f1, friends f2 
WHERE f2.id_user1 = f1.id_user2 
    AND f1.is_accepted 
    AND NOT f1.is_blocked 
    AND f2.is_accepted 
    AND NOT f2.is_blocked 

我希望你不usind的MySQL,因为MySQL是在查询视图上的速度很慢。

0
select id_user1 from friends where is_accepted = 1 and is_blocked = 0 and id_user2 in  
(select id_user1 from friends where is_accepted = 1 and is_blocked = 0 and id_user2 = :a_user: 
    union 
    select id_user2 from friends where is_accepted = 1 and is_blocked = 0 and id_user1 = :a_user:) 
union 
select id_user2 from friends where is_accepted = 1 and is_blocked = 0 and id_user1 in 
(select id_user1 from friends where is_accepted = 1 and is_blocked = 0 and id_user2 = :a_user: 
    union 
    select id_user2 from friends where is_accepted = 1 and is_blocked = 0 and id_user1 = :a_user:) 

您可以添加whereClause淘汰:a_user:从结果。

4

由于其他人提到的原因,并且因为我已经看到它在其他系统中运行得更好,所以我会为每个方向连续行。

primary_user_id | related_user_id | is_accepted | is_blocked | created_at 

然后你也可以创建在USER_ID应足以抵消加倍行数的开销聚集索引。然后

你的第一个查询将转化为这样的事情:

SELECT f1.related_user_id 
FROM friends f1 
WHERE f1.primary_user_id = @current_user 
AND f1.is_accepted = 1 AND f1.is_blocked = 0 
AND EXISTS (
    SELECT * 
    FROM friends f2 
    WHERE f1.related_user_id = f2.primary_user_id 
    AND f2.related_user_id = @current_user 
    AND f2.is_accepted = 1 AND f2.is_blocked = 0 

不知道你是否能在MySQL做表函数。如果是这样,然后将其包装到一个函数中,以使您的第二个查询更简单。

+0

我会研究这个解决方案。谢谢。 – brpaz 2011-04-27 08:23:48