查找重复项需要很长时间

时间:2014-03-20 14:50:01

标签: sql sql-server tsql sql-server-2012

我有以下表格布局:四个不同的表格,每个表格包含大约1000万到1500万个条目。每个表的三个字符串属性是相同的(让我们称它们为Id,Name1,Name2)。现在我们要读取具有相同Id列但不同(Name1,Name2)元组的所有条目。据估计,所有条目中只有不到0.5%是匹配的。

我们创建了一个视图AllEntries(基本上是所有四个表中UNION ALL的相关属性),我们的查询如下所示:

SELECT *
FROM AllEntries
GROUP BY Id
HAVING COUNT(DISTINCT(Name1)) > 1 OR COUNT(DISTINCT(Name2)) > 1

在我们的测试数据库中执行查询,每个表中有200万个条目(即视图中的800万个条目)已经需要大约2到3分钟(不错的服务器)。

问:是否可以改善性能?

4 个答案:

答案 0 :(得分:2)

尝试使用ROW_NUMBER()代替传统GROUP BY/HAVING方法的CTE:

;with cteDups as
(
    Select  *
            ,ROW_NUMBER() Over(Partition By Name1 Order By Id) rn1
            ,ROW_NUMBER() Over(Partition By Name2 Order By Id) rn2
    From    AllEntries
)
Select  *
From    cteDups
Where   rn1 > 1
    Or  rn2 > 1

答案 1 :(得分:1)

count(distinct)比其他聚合函数更耗费资源。你可以尝试:

SELECT *
FROM AllEntries
GROUP BY Id
HAVING min(Name1) <> max(Name1) or min(Name2) <> max(Name2);

如果您在每个子表中的id, Name1id, Name2上构建索引,则以下内容应显示出显着的性能提升:

select ae.*
from AllEntries ae
where exists (select 1 from subtable1 ae2 where ae.id = ae2.id and ae.Name1 <> ae2.Name1) or
      exists (select 1 from subtable2 ae2 where ae.id = ae2.id and ae.Name1 <> ae2.Name1) or
      . . . 

将它们拆分为子查询,以鼓励优化器在每个子查询上使用不同的索引。

答案 2 :(得分:0)

这在很大程度上取决于你的索引,但是对于这个大小的表来说,具有OR条件的最后一个语句肯定不是理想的。另外,我不确定为什么你需要一个SELECT *来...额外的IO。如果可以,请避免使用它。

尝试这样的事情......

SELECT id, COUNT(name1)
FROM {table}
GROUP BY id
HAVING COUNT(*) > 1

UNION ALL


SELECT id, COUNT(name2)
FROM {table}
GROUP BY id
HAVING COUNT(*) > 1

UNION ALL 

这将允许您利用ID上的索引并避免使用COUNT DISTINCT,这是一项非常昂贵的功能。

如果您想获取这些特定记录,我建议您编写CTE并将上述查询的结果与数据本身相结合....返回所有ID和计数的名称(*)更高。

对于您希望以某种方式重复的记录很少,另一个选项就是

SELECT id, COUNT(*)
FROM {table}
GROUP BY id
HAVING COUNT(*) > 1

然后使用id将你的数据表连接起来....这样就可以避免使用union所有内容的额外工作,并向你展示每个记录哪些ID重复。对我而言,无论如何......你可能不想要重复的ID: - )

答案 3 :(得分:0)

UNION正在杀死它 在联合之后,name1或name2上没有使用索引 无法使用UNION

在视图上创建索引

你会认为这已被打破,但试一试 它使用索引并支持少量匹配 如果你没有关于word1和word2的索引,那么就这样做

select distinct ta.ID 
  from t1 as ta
  join t1 as tb
    on ta.ID = tb.ID 
   and ( (ta.word1 <> tb.word1) or (ta.word2 <> tb.word2) )
union 
select distinct ta.ID 
  from t1 as ta
  join t2 as tb
    on ta.ID = tb.ID 
   and ( (ta.word1 <> tb.word1) or (ta.word2 <> tb.word2) )
union 
  t1 t3
union 
  t1 t4
union 
  t2 t2
union 
  t2 t3
union 
  t2 t4
union 
  t3 t3
union 
  t3 t4
union
  t4 t4