【问题标题】:SQL - remove duplicates from left joinSQL - 从左连接中删除重复项
【发布时间】:2013-01-14 22:04:31
【问题描述】:

我正在创建两个表的连接视图,但我从 table2 中得到了不需要的重复项。
例如:table1 有 9000 条记录,我需要结果视图包含完全相同的记录; table2 可能有多个具有相同 FKID 的记录,但我只想返回一条记录(我的客户可以随机选择)。我有以下代码可以正常工作,但性能比预期慢(超过 14 秒)。

SELECT     
    OBJECTID
    , PKID
    ,(SELECT TOP (1) SUBDIVISIO
        FROM dbo.table2 AS t2
        WHERE (t1.PKID = t2.FKID)) AS ProjectName
    ,(SELECT TOP (1) ASBUILT1
        FROM dbo.table2 AS t2
        WHERE (t1.PKID = t2.FKID)) AS Asbuilt
FROM dbo.table1 AS t1

有没有办法对连接做类似的事情来提高性能?
我正在使用 SQL Server 2008 R2。
我接近了以下代码(约 0.5 秒),但 'Distinct' 仅在所有列都重复时过滤掉记录(而不仅仅是 FKID)。

SELECT
    t1.OBJECTID
    ,t1.PKID
    ,t2.ProjectName
    ,t2.Asbuilt
FROM dbo.table1 AS t1
    LEFT JOIN (SELECT
        DISTINCT FKID
        ,ProjectName
        ,Asbuilt
        FROM dbo.table2) t2
    ON t1.PKID = t2.FKID

表格示例

table1          table2

OID, PKID       FKID, ProjectName, Asbuilt
1, id1          id1, P1, AB1
2, id2          id1, P5, AB5
3, id4          id2, P10, AB2
5, id5          id5, P4, AB4

在上面的示例中,返回的记录应该是 id5/P4/AB4、id2/P10/AB2 和 (id1/P1/AB1 OR id1/P5/AB5)

我的搜索提出了类似的问题,但没有一个能解决我的问题。 link, link
在此先感谢您的帮助。这是我的第一篇文章,如果我违反了任何规则,请告诉我。

【问题讨论】:

  • 欢迎来到 StackOverflow。发布问题时,您还应该包括您正在使用的数据库引擎。如果一个答案解决了你的问题,那么你应该接受这个答案。您还可以根据需要对任意数量的答案进行投票。
  • 不会在第一个 SELECT 中添加 DISTINCT 来解决这个问题吗?

标签: sql sql-server join


【解决方案1】:

您的原始查询正在为两列生成任意值(使用top 而没有order by)。你可以用这个得到同样的效果:

SELECT t1.OBJECTID, t1.PKID, t2.ProjectName, t2.Asbuilt
FROM dbo.table1 t1 LEFT JOIN
     (SELECT FKID, min(ProjectName) as ProjectName, MIN(asBuilt) as AsBuilt
      FROM dbo.table2
      group by fkid
     ) t2
    ON t1.PKID = t2.FKID

此版本将distinct 替换为group by

要在 SQL Server 中获得真正随机的行(您的语法建议您正在使用该行),请尝试以下操作:

SELECT t1.OBJECTID, t1.PKID, t2.ProjectName, t2.Asbuilt
FROM dbo.table1 t1 LEFT JOIN
     (SELECT FKID, ProjectName, AsBuilt,
             ROW_NUMBER() over (PARTITION by fkid order by newid()) as seqnum
      FROM dbo.table2
     ) t2
    ON t1.PKID = t2.FKID and t2.seqnum = 1

假设版本为 2005 或更高版本。

【讨论】:

  • 谢谢戈登!我尝试了 option1,但该组一直失败 b/c 其他字段未聚合。我也尝试了 option2 但无法让它工作。由于输出一致,我将选择 option1。
  • 抱歉,我必须取消选中您的答案。子选择中的“MIN”实际上混合了记录之间的值,我更喜欢返回完整的记录。这样客户就不会对需要更新的记录感到困惑。我很感激教育!
  • 我认为值得注意的是,当针对视图运行时,您的 option2 比其他查询快得多。 (364ms vs 7581ms 和 6891ms)
  • @RickMomsen 。 . .您的原始查询也混合了结果,这就是我这样写的原因(我试图在第一句话中解释这一点)。有点意外的是第二个版本跑得更快了,但是窗口函数被高度优化了。
  • 你说得对,戈登。在我调查结果并知道这将是一个问题之前,性能问题阻止了我。再次感谢!
【解决方案2】:

这将提供您要求的结果,并且应该具有最佳性能。

SELECT     
    OBJECTID
    , PKID
    , t2.SUBDIVISIO,
    , t2.ASBUILT1

FROM        dbo.table1 AS t1
OUTER APPLY (
    SELECT  TOP 1 *
    FROM    dbo.table2 AS t2
    WHERE   t1.PKID = t2.FKID
    ) AS t2

【讨论】:

  • 这也有效!我将不得不进一步研究 OUTER APPLY。
  • @RickMomsen 只是出于好奇,现在有多快?
  • 这是我的解决方案,因为它始终返回整条记录。
  • 6891ms 与 7581ms 相比(我的子选择查询)。我应该注意,我必须针对视图而不是源表运行它。当针对视图或源表运行时,您的查询比其他查询(到目前为止提到的)要快;除了 Gordon 的 option2 在 table 或 view 之间几乎没有性能差异
【解决方案3】:

如果你想要描述的结果,你需要使用INNER JOIN,下面的查询将满足你的需要:

SELECT
  t1.OID,
  t1.PKID,
  MAX(t2.ProjectName) AS ProjectName,
  MAX(t2.Asbuilt) AS Asbuilt
FROM table1 t1
JOIN table2 t2 ON t1.PKID = t2.FKID
GROUP BY
  t1.OID,
  t1.PKID

如果您想查看左表 (table1) 中的所有行是否在右表中有对,则使用LEFT JOIN,相同的查询将为您提供所需的结果。

已编辑

这种构造性能很好,不需要使用子查询。

【讨论】:

  • 这也有效,但与上述 Gordon 的 Option1 有相同的 MIN/MAX 问题。
  • 我同意,但我使用的是简单的 JOIN 而不是派生表。
猜你喜欢
  • 1970-01-01
  • 2014-07-21
  • 2017-07-02
  • 2011-05-13
  • 2012-07-05
  • 1970-01-01
  • 2021-03-20
  • 1970-01-01
  • 2019-05-30
相关资源
最近更新 更多