【问题标题】:Comparing SQL Table to itself (Self-join)将 SQL 表与其自身进行比较(自联接)
【发布时间】:2010-12-25 19:31:44
【问题描述】:

我正在尝试根据混合列查找重复行。这是我所拥有的一个例子:

CREATE TABLE Test
(
   id INT PRIMARY KEY,
   test1 varchar(124),
   test2 varchar(124)
)

INSERT INTO TEST ( id, test1, test2 ) VALUES ( 1, 'A', 'B' )
INSERT INTO TEST ( id, test1, test2 ) VALUES ( 2, 'B', 'C' )

现在如果我运行这个查询:

SELECT [LEFT].[ID] 
FROM [TEST] AS [LEFT] 
   INNER JOIN [TEST] AS [RIGHT] 
   ON [LEFT].[ID] != [RIGHT].[ID] 
WHERE [LEFT].[TEST1] = [RIGHT].[TEST2]

我希望取回两个 ID。 (1 和 2),但是我只拿回一排。

我的想法是它应该比较每一行,但我想这不正确? 为了解决这个问题,我将查询更改为:

SELECT [LEFT].[ID] 
FROM [TEST] AS [LEFT] 
   INNER JOIN [TEST] AS [RIGHT] 
   ON [LEFT].[ID] != [RIGHT].[ID] 
WHERE [LEFT].[TEST1] = [RIGHT].[TEST2] 
OR [LEFT].[TEST2] = [RIGHT].[TEST1]

这给了我两行,但是根据行数,性能下降得非常快。

我为性能和结果提出的最终解决方案是使用联合:

SELECT [LEFT].[ID] 
FROM [TEST] AS [LEFT] 
   INNER JOIN [TEST] AS [RIGHT] 
   ON [LEFT].[ID] != [RIGHT].[ID] 
WHERE [LEFT].[TEST1] = [RIGHT].[TEST2] 
UNION
SELECT [LEFT].[ID] 
FROM [TEST] AS [LEFT] 
   INNER JOIN [TEST] AS [RIGHT] 
   ON [LEFT].[ID] != [RIGHT].[ID] 
WHERE [LEFT].[TEST2] = [RIGHT].[TEST1]

但总的来说,我显然不明白为什么这不起作用,这意味着我可能做错了什么。有人能指出我正确的方向吗?

【问题讨论】:

  • 尝试使用以下数据进行第一个查询: INSERT INTO TEST ( id, test1, test2 ) VALUES ( 1, 'C', 'B' ) INSERT INTO TEST ( id, test1, test2 ) VALUES ( 2, 'B', 'C' ) 那应该给你两行。

标签: sql join self-join


【解决方案1】:

不要加入不等式;看来 JOIN 和 WHERE 条件是相反的。

SELECT t1.id
FROM Test t1
INNER JOIN Test t2
ON ((t1.test1 = t2.test2) OR (t1.test2 = t2.test1))
WHERE t1.id <> t2.id

应该可以正常工作。

【讨论】:

  • 您好,从一些测试来看,这似乎仍然比使用联合要慢 :( 永远不加入不等式的原因是什么?where 语句不一样吗?(尽管您的加入可能返回较少行比其他行多,可能会加快查询速度。这是原因吗?)
  • 在我的测试中,UNION 版本占用了 3 倍以上的时间。你是如何测试的?不加入不等式的原因是优化器必须读取满足该条件的每一行(即几乎所有行)并随后过滤;此版本可以使用列 test1 或 test2 或两者上的索引。除非优化器以某种方式重写您的查询,否则如果您将此版本与正确的索引一起使用,您应该会看到巨大的性能提升。
  • 实际上,现在我想起来了,由于您的架构似乎没有有用的索引,所以我发布的查询将执行与不等式连接查询相同的操作;不管你做什么,你最终都会得到两个完整的聚集索引扫描,这太可怕了。您需要覆盖 (test1, test2) 和 (test2, test1) 上的索引以获得更好的性能。
【解决方案2】:

如果您选择它们​​,您只能取回两个 id:

SELECT [LEFT].[ID], [RIGHT].[ID] 
FROM [TEST] AS [LEFT] 
   INNER JOIN [TEST] AS [RIGHT] 
   ON [LEFT].[ID] != [RIGHT].[ID] 
WHERE [LEFT].[TEST1] = [RIGHT].[TEST2]

只得到一个ROW的原因是只有一行(即第2行)的TEST1等于另一行的TEST2。

【讨论】:

  • +1 因为您解释了为什么原始语法不起作用。因为你的答案有效。 “这个答案很有用”
【解决方案3】:

我看起来您正在为Cartiesian Join 努力工作。通常,如果您要返回重复项,则需要运行以下命令:

SELECT [LEFT].*
FROM [TEST]  AS [LEFT]
INNER JOIN [TEST] AS [RIGHT]
    ON [LEFT].[test1] = [RIGHT].[test1]
        AND [LEFT].[test2] = [RIGHT].[test2]
        AND [LEFT].[id] <> [RIGHT].[id]

如果您需要混合列,则混合所需的条件,但请执行以下操作:

SELECT [LEFT].*
FROM [TEST] AS [LEFT]
INNER JOIN [TEST] AS [RIGHT]
    ON (
        [LEFT].[test1] = [RIGHT].[test2]
            OR [LEFT].[test2] = [RIGHT].[test1]
       )
        AND [LEFT].[id] <> [RIGHT].[id]

使用它,您可以在每个连接中比较右到左和左到右,从而完全不需要 WHERE。

但是,对于插入到表中的每一行,这种查询方式的执行时间呈指数增长,因为您要对每一行与每一行进行比较。

【讨论】:

    【解决方案4】:

    如果我没记错的话,这可以通过外部连接来完成。 这是我第一次回答 mysql 类的问题,但我只是在 StackOverflow 上回答以获得更多积分。 逗号很重要,这样mysql就不会报错了。

    SELECT [LEFT].[ID] FROM [TEST] AS [LEFT], [TEST] AS [RIGHT] 
    WHERE [LEFT].[ID] != [RIGHT].[ID] 
    AND [LEFT].[TEST1] = [RIGHT].[TEST2];
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-17
      • 1970-01-01
      • 1970-01-01
      • 2023-03-15
      相关资源
      最近更新 更多