0

我知道关于这个主题有很多结果,但它们对我没有帮助。

我有一个包含 user1 和 user2 的朋友表。

真正的朋友是当 user1 是 user2 的朋友并且 user2 是 user1 的朋友时。

好友请求是当 user1 与 user2 成为好友时。它看起来像这样:

 user1 | user2
 -------------
 1     | 2
 2     | 1
 1     | 3
 3     | 1
 1     | 5

查询如何才能找到#1 的真正朋友?

我试过这个但它返回null:

SELECT user2 FROM friends WHERE user1 = 1 AND user2 = 1

此外,查询将如何查找好友请求?

4

3 回答 3

2
SELECT a.user1 FROM friends AS a JOIN friends AS b
  ON a.user2 = b.user1 AND a.user1 = b.user2
  WHERE a.user2 = ?

其中?表示“原始”用户的 ID。

于 2013-08-20T18:53:59.090 回答
0
SELECT user2 FROM friends 
  WHERE user1 = 1 
  AND user2 IN (SELECT user1 from friends where user2 = 1);
于 2013-08-20T18:49:11.573 回答
0

一种方法是使用 JOIN 操作:

SELECT f.user2
  FROM friends f
  JOIN friends r
    ON r.user1 = f.user2
   AND r.user2 = f.user1
 WHERE f.user1 = 1

假设“真正的朋友”关系由两个元组的存在来标识,即1和之间的真正朋友关系n将由表中的两行表示:(1,n)(n,1)

连接条件中的谓词将返回的行限制为具有匹配“反向”元组的那些行。

注意:JOIN 操作通常比等效IN (subquery)EXISTS (subquery)模式执行得更好,但是对于小集合,这种性能差异可以忽略不计。使用更大的集合,性能差异变得明显。

使用 EXISTS 谓词可以返回等效结果(通常效率较低):

SELECT f.user2
  FROM friends f
 WHERE f.user1 = 1
   AND EXISTS ( SELECT 1
                  FROM friends r
                 WHERE r.user1 = f.user2
                   AND r.user2 = f.user1
              )

或 IN 谓词:

SELECT f.user2
  FROM friends f
 WHERE f.user1 = 1
   AND f.user2 IN ( SELECT r.user1
                      FROM friends r
                     WHERE r.user2 = f.user1
                  )

(如果对朋友(user1,user2)没有唯一约束,则 JOIN 可能会返回一些其他查询可能不会返回的重复行,但没有一个查询保证不返回重复项。如果没有唯一约束,并且您不希望返回任何重复项,那么您可以在任何这些语句的开头的 SELECT 之后添加一个 DISTINCT 关键字,或者在任何这些语句的末尾添加一个 GROUP BY f.user2 。

为了使结果集更具确定性(即每次运行查询时返回相同的结果),您可以添加 ORDER BY 子句。(但 GROUP BY 不需要它,因为 MySQL 隐式地对 GROUP BY 表达式执行 ORDER BY。)


跟进

解释我如何将此结果与用户表中的名称绑定?谢谢你。我如何获得“不真实”的朋友?

要从用户表中获取名称,我们只需在用户表中添加一个 JOIN,假设 id 是主键列,user1 和 user2 列是用户表的外键...

SELECT f.user2
     , u.name
  FROM friends f
  JOIN user u
    ON u.id = f.user2
  JOIN friends r
    ON r.user1 = f.user2
   AND r.user2 = f.user1
 WHERE f.user1 = 1

“不真实”的朋友将被表示为一个元组(表中的行)(1,n),它没有相应的逆元组(n,1)。为了找到这些行,我们使用反连接模式,这是一个外连接(从一侧返回所有行加上任何匹配的行),然后是一个谓词,它排除找到匹配的行(检查一个 NULL如果有匹配项,则保证不为空的列是我们这样做的方式):

这将找到(1,n)没有匹配的所有元组(n,1)

SELECT f.user2
     , u.name
  FROM friends f
  JOIN user u 
    ON u.id = f.user2
  LEFT
  JOIN friends r
    ON r.user1 = f.user2
   AND r.user2 = f.user1
 WHERE r.user1 IS NULL
   AND f.user1 = 1

我们必须将其翻转以获取另一侧,(n,1)即没有匹配行的(1,n)行:

SELECT f.user1
  FROM friends f
  JOIN user u 
    ON u.id = f.user1
  LEFT
  JOIN friends r
    ON r.user2 = f.user1
   AND r.user1 = f.user2
 WHERE r.user2 IS NULL
   AND f.user2 = 1
于 2013-08-20T18:52:37.423 回答