根据连接条件,巨大的表运行缓慢的T-SQL查询

时间:2009-10-14 16:10:12

标签: sql sql-server tsql sql-server-2008

我们有一个庞大的公司表(1700万条目),我们希望根据搜索条件(基于电话号码)查找重复项。查询运行速度很慢(5分钟+)

以下是查询的简化版本,但问题是相同的:

SELECT C1.*
FROM dbo.Company AS C1 WITH(NOLOCK)
INNER JOIN dbo.Company AS C2 ON C2.sTelephone = C1.sTelephone 
                         AND C1.iId_company != C2.iId_company 
                         AND (C1.iId_third_party_id IS NULL OR 
                              C2.iId_third_party_id IS NULL)

列说明:

  • iId_company:主键,整数自动增量
  • sTelephone:公司的电话号码,varchar上有非聚集索引
  • iId_third_party_id:来自第三方提供商的ID,当用户自己插入新公司时(因为我们想要查找重复项),也可能为null,也包含非聚集索引的整数。

我们拥有相同电话号码但主键不同(重复)的公司是什么,还有一部分没有第三方ID(告诉我们最终用户插入了它。

现在,我尝试了一些东西,但没有给我任何线索:

  • OR 子句中删除一侧时,只剩下 C1.iId_third_party_id IS NULL 会给出一个巨大的提升,查询时间不到5秒
  • 当使用 OR 子句完全删除括号中的条件时,查询会再次变慢(1分钟+),但我认为这只是因为数据集的返回非常大。 / LI>

我最终让UNION将两个查询组合在一起(每个查询都在 OR 条件下),但我想了解为什么在使用 OR 在这种情况下。

7 个答案:

答案 0 :(得分:2)

找出性能差异的最佳方法是检查您尝试过的查询的执行计划。他们可以提供很多信息。不幸的是,我不是SQL Server专家,所以我不知道如何获得执行计划。

答案 1 :(得分:2)

我不知道这在性能方面是否有任何帮助(因为我手头没有17mio。行表来测试),但是这个怎么样:

  • 选择您绝对需要的最少量信息(不要做SELECT C1。* !!)
  • 按电话号码和计数发生组
  • 需要更密切地检查发生2次或更多次的“电话号码组”

由于您使用的是SQL Server 2008,因此您应该可以创建类似这样的内容(公用表表达式 - CTE)。这应该会缩小搜索范围,因为只有(希望!)公司表中的少数条目确实会重复 - 所以这应该限制你的搜索速度,从而加快搜索速度(或者至少是希望!)。

WITH PhoneDuplicates AS
(SELECT c.Telephone, COUNT(*) as PhoneCount
   FROM dbo.Company AS c 
   GROUP BY c.Telephone
   HAVING COUNT(*) > 1
)
SELECT 
  (list of fields from company table)
FROM
  dbo.Company AS c
INNER JOIN
  PhoneDuplicates as PD ON PD.Telephone = c.Telephone

马克

答案 2 :(得分:1)

性能方面,过滤列的基数是什么?

也许只有

C1.iId_third_party_id IS NULL 

提升了性能,因为SQL可以告诉(根据索引构建的统计信息)相对较少的行符合必要的标准。当你添加

(... OR C2.iId_third_party_id IS NULL)

也许SQL认为结果连接会产生如此多的匹配行,因此在该列上使用索引效率不高。

同样,有多少匹配/重复的电话号码? 如果这种情况非常罕见,我会做类似marc_s的查询(他打败了我),因为它会飞。

很大程度上取决于数据的样子 - 您的过滤条件出现的频率或频率。分析一下,尝试并了解它是如何以及它如何随时间变化,并相应地设计您的查询。

答案 3 :(得分:1)

删除OR部分时看到的速度增加是因为OR自动执行索引扫描而不是搜索。通过将它们结合在一起,你可以做更快的2次寻找。

尝试使用row_number技术找到欺骗:

;with cteDupes(RN, DupeID, DupeTelephone) as
(
SELECT  row_number() over(partition by sTelephone order by iId_company, sTelephone) RN,
        iId_company, sTelephone
FROM    dbo.Company 
WHERE   iId_third_party_id IS NULL
)
select * from cteDupes
where RN > 1

这将只返回被欺骗的行。 这样做的好处是你只能获得一张桌子而不是两张。

答案 4 :(得分:0)

你可以尝试的东西是

SELECT C1.*
FROM (select * from dbo.Company where iId_third_party_id IS NULL) AS C1 WITH(NOLOCK)
INNER JOIN (select * from dbo.Company where iId_third_party_id IS NULL) AS C2 ON C2.sTelephone = C1.sTelephone 
                         AND C1.iId_company != C2.iId_company 

因为这对我们有所帮助。

答案 5 :(得分:0)

我怀疑你的非聚集索引是否会被使用(sTelephoneiId_third_party_id)。您是否在主键上进行聚类?

查看估计的执行计划。

在没有看到计划的情况下,我会考虑将iId_third_party_id添加到sTelephone上的非聚集索引,如果您没有在主键上进行聚类,则添加iId_company也是索引。

请注意,当给定电话号码的重复次数超过两次时,结果可能会出现交叉连接倍增。

答案 6 :(得分:0)

With Temp as
(Select *
FROM dbo.Company as c
Where c.iId_third_party_id is NULL)

Select C1.*
From temp as C1 With (NoLock)
INNER JOIN Temp AS C2 
ON C2.sTelephone = C1.sTelephone AND C1.iId_company != C2.iId_company

这样的事情可能会起作用