如何编写这个复杂的自联接

时间:2013-03-09 21:24:27

标签: mysql join

我有两个带有给定字段的表

  1. HighSchooler

    • 名称
    • 等级
    • ID
  2. 喜欢

    • ID1
    • ID2
  3. HighSchooler持有高中生的信息,Likes是一个关系表,显示谁喜欢高中生。 id1的学生喜欢id2的学生。 既有单向关系(约翰喜欢露西,但露西不喜欢约翰)和双向关系(当阿尔伯特喜欢桑德拉和桑德拉也喜欢艾伯特时)。

    我需要一个返回两个列的查询,这些列的名称是双向关系,即如果A喜欢B和B喜欢A,那么样本结果集将是

    name | name
    A       B
    

    我摆弄它并想出了这个查询,但我不明白它并且不认为它是最佳的。

    SELECT DISTINCT a.name, b.name 
    FROM Highschooler a, Highschooler b, Likes l1 
    JOIN Likes l2 on l1.ID1=l2.ID2 
    WHERE a.ID=l1.ID2 AND b.ID=l1.ID1 AND a.ID=l2.ID1 AND a.ID > b.ID;
    

3 个答案:

答案 0 :(得分:3)

尝试使用规则(l1.id1 = l2.id2) and (l1.id2 = l2.id1)

加入Likes表

示例:

SELECT
   a.name AS a_name,
   b.name AS b_name
FROM
   HighSchooler AS a
   INNER JOIN Likes AS l1
      ON (a.id = l1.id1)
   INNER JOIN Likes AS l2
      ON ((l1.id1 = l2.id2) AND (l1.id2 = l2.id1) AND (l1.id1 > l2.id1))
   INNER JOIN HighSchooler AS b
      ON (l2.id1 = b.id)

http://sqlfiddle.com/#!2/44a07/6

答案 1 :(得分:1)

您的查询是正确的,但它正在使用表格的笛卡尔积,正如您所说的那样不是最优的。当您编写select * from a,b时,a的所有行和b的所有行组合在一起以形成具有大小(a)* size(b)行的新表。你正在用三个表做这个,所以你创建一个巨大的表,然后从中选择你想要的几行。内部联接可以更有效地完成此任务。

SELECT
a.name AS name_a, b.name AS name_b
FROM
HighSchooler AS a
INNER JOIN Likes AS l1
ON a.id = l1.id1
INNER JOIN Likes AS l2
ON l1.id1 = l2.id2 AND l1.id2 = l2.id1 AND l1.id1 < l1.id2
INNER JOIN HighSchooler AS b
ON l1.id2 = b.id;

请参阅fiddle

答案 2 :(得分:1)

关键的自连接是在对Likes表的两个引用之间。然后需要将其连接两次到HighSchoolers表以获取两个人的名字。

步骤1每对ID

的ID对
SELECT l1.id1, l1.id2
  FROM Likes AS l1
  JOIN Likes AS l2
    ON l1.id1 = l2.id2 AND l1.id2 = l2.id1;

这给出了ID对的列表,其中每个ID都是另一个。

步骤2成对的ID,其中每个都喜欢另一个而不重复

唯一的障碍是它给每对两次。因此,诀窍是要注意,在两行之一中,id1值小于id2值。作为一种可能有益的副作用,这可以消除任何喜欢自己的人。

SELECT l1.id1, l1.id2
  FROM Likes AS l1
  JOIN Likes AS l2
    ON l1.id1 = l2.id2 AND l1.id2 = l2.id1;
 WHERE l1.id1 < l1.id2

第3步名称对,其中每个人都喜欢另一个

现在用名字整理一下:

SELECT h1.name AS name1, h2.name AS name2
  FROM (SELECT l1.id1, l1.id2
          FROM Likes AS l1
          JOIN Likes AS l2
            ON l1.id1 = l2.id2 AND l1.id2 = l2.id1
         WHERE l1.id1 < l1.id2
       ) AS p
  JOIN HighSchoolers AS h1 ON p.id1 = h1.id
  JOIN HighSchoolers AS h2 ON p.id2 = h2.id

p是'对'的助记符。