【问题标题】:T-SQL query on huge table running slow depending on join conditions大型表上的 T-SQL 查询运行缓慢,具体取决于连接条件
【发布时间】:2010-12-06 18:02:05
【问题描述】:

我们有一个庞大的公司表(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,当用户插入自己的新公司时可能为空(为此我们要查找重复项),它也是带有非聚集索引的整数。

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

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

  • OR 子句中删除一侧时,仅剩余的 C1.iId_third_party_id IS NULL 会产生巨大的提升,查询时间不到 5 秒
  • 当用 OR 子句完全删除括号中的条件时,查询又变慢了(1 分钟 +),但我认为这只是因为返回的数据集非常庞大。

我最终使 UNION 将两个查询结合起来(每个查询都有其在 OR 条件下的部分),但我想了解为什么在使用该 OR 时会有如此大的差异strong> 在条件下。

【问题讨论】:

  • 我尝试在单个查询中完成此操作,但似乎非常需要保持良好的性能,并将其放在单个查询中。我使用 UNION 的解决方案似乎相当不错,因为到目前为止所有答案都使用多个查询来完成此操作。

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


【解决方案1】:

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

【讨论】:

  • 我已经看过执行计划,但它没有给我线索。我不太习惯分析执行计划。
  • 那很不幸。因为在处理大量数据时,执行计划可以在查询优化方面给出很多提示(要添加的附加索引、查询提示等)
【解决方案2】:

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

  • 选择您绝对需要的最少信息量(不要执行 SELECT C1.* !!)
  • 按电话号码分组并统计出现次数
  • 任何出现 2 次或以上的“电话号码组”都需要更仔细地检查

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

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

马克

【讨论】:

    【解决方案3】:

    就性能而言,您的过滤列的基数是多少?

    也许只有

    C1.iId_third_party_id IS NULL 
    

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

    (... OR C2.iId_third_party_id IS NULL)
    

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

    同样,会有多少个匹配/重复的电话号码? 如果这种情况很少发生,我会做类似 marc_s 的查询(他比我强),因为那会飞起来。

    很大程度上取决于数据的外观——您的过滤条件出现的频率或频率。分析它,尝试了解它是如何随着时间而变化的,并相应地设计您的查询。

    【讨论】:

    • 当查询真正结束时,它返回 10000 到 20000 行
    【解决方案4】:

    您在删除 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
    

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

    【讨论】:

      【解决方案5】:

      你可以试试

      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 
      

      因为这曾经帮助过我们。

      【讨论】:

      • 我认为这行不通,因为您的子查询创建了一个“AND”,当只有其中一个需要为 NULL 才能连接时,只返回两个都具有 NULL 的结果成功。
      【解决方案6】:
      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
      

      这样的事情可能会奏效

      【讨论】:

        【解决方案7】:

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

        查看估计的执行计划。

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

        请注意,当给定电话号码有两个以上重复时,结果也可能会交叉加入。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-05-16
          • 2015-10-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-01
          相关资源
          最近更新 更多