【问题标题】:Best self join technique when checking for duplicates检查重复项时的最佳自连接技术
【发布时间】:2011-05-02 15:43:52
【问题描述】:

我正在尝试优化生产中需要很长时间的查询。目标是根据匹配的字段值条件查找重复记录,然后将其删除。当前查询通过 t1.col1 = t2.col1 上的内部连接使用自连接,然后使用 where 子句检查值。

select * from table t1 
inner join table t2 on t1.col1 = t2.col1
where t1.col2 = t2.col2 ...

有什么更好的方法来做到这一点?还是基于索引都一样?也许

select * from table t1, table t2
where t1.col1 = t2.col1, t2.col2 = t2.col2 ...

此表有 100m+ 行。

MS SQL、SQL Server 2008 企业版

select distinct t2.id
    from table1 t1 with (nolock)
    inner join table1 t2 with (nolock) on  t1.ckid=t2.ckid
    left join table2 t3 on t1.cid = t3.cid and t1.typeid = t3.typeid
    where 
    t2.id > @Max_id and
    t2.timestamp > t1.timestamp and
    t2.rid = 2 and
    isnull(t1.col1,'') = isnull(t2.col1,'') and 
    isnull(t1.cid,-1) = isnull(t2.cid,-1) and
    isnull(t1.rid,-1) = isnull(t2.rid,-1)and 
    isnull(t1.typeid,-1) = isnull(t2.typeid,-1) and
    isnull(t1.cktypeid,-1) = isnull(t2.cktypeid,-1) and
    isnull(t1.oid,'') = isnull(t2.oid,'') and
    isnull(t1.stypeid,-1) = isnull(t2.stypeid,-1)  

    and (
            (
                t3.uniqueoid = 1
            )
            or
            (
                t3.uniqueoid is null and 
                isnull(t1.col1,'') = isnull(t2.col1,'') and 
                isnull(t1.col2,'') = isnull(t2.col2,'') and
                isnull(t1.rdid,-1) = isnull(t2.rdid,-1) and 
                isnull(t1.stid,-1) = isnull(t2.stid,-1) and
                isnull(t1.huaid,-1) = isnull(t2.huaid,-1) and
                isnull(t1.lpid,-1) = isnull(t2.lpid,-1) and
                isnull(t1.col3,-1) = isnull(t2.col3,-1) 
            )
    )

【问题讨论】:

  • MS SQL?如果有,是哪个版本的 SQL Server?
  • @Bruno 抱歉,我已经更新了问题和标签
  • @Mitch 不,这是一张加入同一张桌子的桌子。怎么不是自加入?
  • @Mitch 不是都叫table
  • 当然,调用表“table”可能会产生语法错误。而“create table table ...”听起来很傻,就像老利比的广告一样。但这只是一个例子。

标签: sql sql-server-2008


【解决方案1】:

为什么要自我加入:这是一个综合问题。

希望你在 col1、col2、...上有一个索引

--DELETE table
--WHERE KeyCol NOT IN (
select
    MIN(KeyCol) AS RowToKeep,
    col1, col2, 
from
    table
GROUP BY
    col12, col2
HAVING
   COUNT(*) > 1
--)

但是,这需要一些时间。有一个look at bulk delete techniques

【讨论】:

  • 我已经用查询更新了我的问题(列/表名称已更改)。您的建议是否仍然有效?
  • @Titan278:应该这样做。无需使用 ISNULL 进行 GROUP BY,您应该使用 EXISTS 进行 table2/t3 检查(避免使用 DISTINCT)
【解决方案2】:

您可以使用 ROW_NUMBER() 在一个表中查找重复的行。

您可以查看here

【讨论】:

  • 哦。对这种大小的桌子会很讨厌。我会使用聚合
  • 是的,但如果行完全相同,我认为聚合不会起作用,至少以简单的方式。
  • 您假设没有 PK:您可以在 PK 之外有重复项。 OP 的更新显示有一个 PK 但仍然重复。这不会改变 ROW_NUMBER 会像狗一样奔跑的事实。
  • 好吧,在示例中我们可以看到他加入了 t1.col1 = t2.col1 并且其中 col2 = col2 (等等)。所以我假设行中的每个字段都是相等的。如果你想在那种情况下操纵数据,我认为 Row_number 是最好的,如果它像狗一样跑(与聚合相比确实如此)。
  • select distinct t2.id... 在重复检查之外显示一个 id 列。性能确实会有所不同,因为有可用的窗口来进行数据更改(使用备份,考虑错误时的回滚时间等)。我有十亿多行表和 24/6 操作:我最后一次大型 DML 清理花了大约 13 个小时:算出我有多少空闲时间......
【解决方案3】:

你给出的两种方法应该是等价的。我认为大多数 SQL 引擎在这两种情况下都会做完全相同的事情。

而且,顺便说一句,这行不通。您必须至少有一个不同的字段,否则每条记录都会匹配自己。

您可能想尝试更多类似的方法:

select col1, col2, col3
from table
group by col1, col2, col3
having count(*)>1

【讨论】:

    【解决方案4】:

    对于 100m+ 行的表,使用 GROUPBY 函数和使用保持表将被优化。即使它转化为四个查询。

    第 1 步:创建一个保持键:

    SELECT col1, col2, col3=count(*)
    INTO holdkey
    FROM t1
    GROUP BY col1, col2
    HAVING count(*) > 1
    

    第 2 步:将所有重复的条目推送到 holddups。这是第 4 步所必需的。

    SELECT DISTINCT t1.*
    INTO holddups
    FROM t1, holdkey
    WHERE t1.col1 = holdkey.col1
    AND t1.col2 = holdkey.col2
    

    第 3 步:从原始表中删除重复的行。

    DELETE t1
    FROM t1, holdkey
    WHERE t1.col1 = holdkey.col1
    AND t1.col2 = holdkey.col2
    

    第 4 步:将唯一行放回原始表中。例如:

    INSERT t1 SELECT * FROM holddups
    

    【讨论】:

      【解决方案5】:

      要检测重复项,您无需加入:

      SELECT col1, col2
      FROM table
      GROUP BY col1, col2
      HAVING COUNT(*) > 1
      

      那应该快得多。

      【讨论】:

      • 在一亿行的表上?我不这么认为!
      • @Mitch Wheat:任何技术都会像狗一样跑超过 100m + 行...尤其是基于 ROW_NUMBER 的函数
      • @Mitch:实际的问题不是,“有没有一种方法可以在 undex x 秒内运行?”但是“给出所需结果的最快查询是什么。”
      • @gbn:我不同意:有些技术是吉娃娃,有些是罗威纳。我在哪里提到 ROW_NUMBER()?
      【解决方案6】:

      根据我的经验,OR 条件下的 SQL Server 性能确实很差。可能不是自连接而是与 table3 导致性能不佳的连接。但是没有看到计划,我不确定。

      在这种情况下,将查询分成两部分可能会有所帮助: 一个带有 WHERE 条件 t3.uniqueoid = 1,一个带有 WHERE 条件用于 table3 上的其他条件,然后使用 UNION ALL 将一个附加到另一个。

      【讨论】:

        猜你喜欢
        • 2013-07-06
        • 1970-01-01
        • 2011-10-04
        • 1970-01-01
        • 1970-01-01
        • 2017-05-17
        • 1970-01-01
        • 2017-05-24
        相关资源
        最近更新 更多