INNER JOIN表本身

时间:2014-02-09 18:40:27

标签: mysql sql database join inner-join

我有一张表格,代表两个用户之间的友谊。每个条目都是单向的;友谊需要两个条目来表示。我想保持这种方式。

user1_id | user2_id 
   43        44
   44        43

我很好奇查询这种设置的最佳方法是什么。例如,如何查询特定用户的所有朋友的列表?

我提出的解决方案是两个反转和内部将friendships表连接到自身,首先获得所有完整对的列表,然后使用正常的WHERE子句:

SELECT f.user2_id 
    FROM friendships f
        INNER JOIN friendships f2
            ON f.user1_id = f2.user2_id 
            && f.user2_id = f2.user1_id
        WHERE f.user1_id = 43;

为了进入多汁的情况,我还需要能够查询以获取由用户的朋友创建的线程列表。我仍然在适应使用连接所需的抽象思维,但我提出的解决方案是:

SELECT thread_id,owner_id,message,time  
    FROM threads th                             
        INNER JOIN friendships f                                
            ON th.owner_id = f.user2_id                             
            && f.user1_id = 43                           
        INNER JOIN friendships f2                               
            ON f.user2_id = f2.user1_id                             
            && f.user1_id = f2.user2_id

这似乎有效,但我正在一个非常空的数据库上测试它。由于我对连接的掌握仍然有点弱,我担心在某些情况下会出现不良后果。所以我的问题是:

  1. 这两个查询是否符合我的预期?
  2. 有更好的方法可以解决这个问题吗?

1 个答案:

答案 0 :(得分:4)

让我们开始通过创建一个查询来回答这个问题,该查询给出了所有(用户,朋友)对的列表。你要求朋友对是互惠关系,所以我们需要这样做。否则,friendships表将提供这些对。

您的查询非常接近。此查询所需的是内连接。

SELECT f1.user1_id AS user, 
       f1.user2_id AS friend 
  FROM friendships AS f1
  JOIN friendships f2 
    ON  (f1.user1_id = f2.user2_id AND f1.user2_id = f2.user1_id)

然后,您可以将此查询用作虚拟表。例如,您可以执行此操作以获取用户43的朋友列表。

 SELECT friend
   FROM (
        SELECT f1.user1_id AS user, 
               f1.user2_id AS friend 
          FROM friendships AS f1
          JOIN friendships f2 
            ON  (f1.user1_id = f2.user2_id AND f1.user2_id = f2.user1_id)
        ) AS friends 
  WHERE user = 43 

您可能希望将您的朋友查询设置为视图,就像这样。

 CREATE VIEW friends AS (
        SELECT f1.user1_id AS user, 
               f1.user2_id AS friend 
          FROM friendships AS f1
          JOIN friendships f2 
            ON  (f1.user1_id = f2.user2_id AND f1.user2_id = f2.user1_id)
 )

这样你就可以像这样缩写复杂的查询。

 SELECT friend FROM friends WHERE user = 43;

您的多汁查询也很容易:

SELECT thread_id,owner_id,message,time  
  FROM threads AS th
  JOIN (
        SELECT f1.user1_id AS user, 
               f1.user2_id AS friend 
          FROM friendships AS f1
          JOIN friendships f2 
            ON  (f1.user1_id = f2.user2_id AND f1.user2_id = f2.user1_id)

       ) AS f ON th.owner_id = f.friend
 WHERE f.user = 43

如果您使用视图,则可以执行此操作。这意味着同样的事情,但更容易阅读。

SELECT thread_id,owner_id,message,time  
  FROM threads AS th
  JOIN friends AS f ON th.owner_id = f.friend
 WHERE f.user = 43

(注意:JOININNER JOIN是同义词。)

看看怎么样?您可以封装friends查询(作为视图或仅作为虚拟表内联)并使用其结果。当您指定WHERE f.user = 43等内容时,优化器会知道如何快速执行此操作。

如果friendships表中有很多行,您可能会发现一对复合索引(user1_id, user2_id)(user2_id, user1_id)有助于这些查询的执行

(这不是递归查询,尽管有相反的评论。)