修改
该查询会将朋友表中一行的“单向”关系视为“双向”关系。也就是说,它会考虑一个朋友关系:('abc','xyz')
等价于逆关系:('xyz','abc')
。(注意:我们不能保证两行都不会出现在表中,所以我们需要注意这一点。UNION
运算符方便地为我们消除重复。)
此查询应满足规范:
SELECT mf.id
, mf.name
FROM (
SELECT fr.user_id AS user_id
, fr.friend_id AS friend_id
FROM friend fr
JOIN users fru
ON fru.id = fr.user_id
WHERE fru.name IN ('abc','xyz')
UNION
SELECT fl.friend_id AS user_id
, fl.user_id AS friend_id
FROM friend fl
JOIN users flf
ON flf.id = fl.friend_id
WHERE flf.user IN ('abc','xyz')
) f
JOIN users mf
ON mf.id = f.friend_id
GROUP BY mf.id, mf.name
HAVING COUNT(1) = 2
ORDER BY mf.id, mf.name
SQL Fiddle 在这里http://sqlfiddle.com/#!2/b23a5/2
下面给出了我们如何达到这一点的更详细的解释。下面的原始查询假设朋友表中的一行表示“单向”关系,因为“ 'abc' ff 'xyz'
”并不意味着“ 'xyz' ff 'abc'
”。但 OP 的其他评论暗示情况并非如此。
如果 有唯一约束friend(user_id,friend_id)
,那么获得结果的一种方法是获取每个用户的所有朋友,并获取该朋友的行数。friend_id
如果计数是 2,那么我们知道用户 'abc' 和 'xyz' 都出现了一个特定的
SELECT mf.id
, mf.name
FROM friend f
JOIN users uu
ON uu.id = f.user_id
JOIN users mf
ON mf.id = f.friend_id
WHERE uu.name IN ('abc','xyz')
GROUP BY mf.id, mf.name
HAVING COUNT(1) = 2
ORDER BY mf.id, mf.name
(这种方法也可以扩展到找到三个或更多用户的共同朋友,方法是在 IN 列表中包含更多用户,并更改我们比较 COUNT(1) 的值。
这不是返回指定结果集的唯一查询;还有其他方法可以得到它。
获得等效结果的另一种方法:
SELECT u.id
, u.name
FROM ( SELECT f1.friend_id
FROM friend f1
JOIN users u1
ON u1.id = f1.user_id
WHERE u1.name = 'abc'
) t1
JOIN ( SELECT f2.friend_id
FROM friend f2
JOIN users u2
ON u2.id = f2.user_id
WHERE u2.name = 'xyz'
) t2
ON t2.friend_id = t1.friend_id
JOIN users u
ON u.id = t1.friend_id
ORDER BY u.id, u.name
笔记
这些查询不检查用户“abc”是否是“xyz”(WHERE 子句中指定的两个用户名)的朋友。它只是找到'abc'和'xyz'的共同朋友。
跟进
上面的查询满足指定的要求,以及问题中提供的所有示例和测试用例。
现在听起来好像您希望将该关系表中的一行视为“双向”关系,而不仅仅是“单向”关系。听起来您想考虑将朋友关系 ('abc','xyz') 等同于 ('xyz','abc')。
要做到这一点,需要做的就是让查询创建反向行,这使得查询更容易。我们只需要注意,如果这两个行 ('abc','xyz') 和 ('xyz','abc') 都已经存在,那么我们在反转它们时不会创建它们的副本。
要创建反向行,我们可以使用这样的查询。(当我们没有 JOIN 到 users 表时,看这个更简单,我们只使用 id 值:
SELECT fr.user_id
, fr.friend_id
FROM friend fr
WHERE fr.user_id IN (1,2)
UNION
SELECT fl.friend_id AS user_id
, fl.user_id AS friend_id
FROM friend fl
WHERE fl.friend_id IN (1,2)
如果我们不包括 user_id 和friend_id 表中的谓词会更简单,但这可能是一个非常大(且成本高昂)的行集来实现。