【问题标题】:SQL WHERE clause performance degradationSQL WHERE 子句性能下降
【发布时间】:2012-07-02 06:52:41
【问题描述】:

我正在处理一个查询,它显示了我没有预料到的性能问题。这是到目前为止的查询。

INSERT INTO @Bridge (PolicyNumber, ProducerCode, BridgeDate, EffectiveDate, FirstName, LastName, LicenseNumber, BirthDate, Address, City, State, ZipCode)
SELECT     tab.col.value('@PolicyNumber', 'VARCHAR(10)') AS PolicyNumber,
           tab.col.value('@ProducerCode','VARCHAR(10)') as ProducerCode,
           tab.col.value('@BridgeDate','DATETIME') AS BridgeDate,
           tab.col.value('@EffectiveDate', 'DATETIME') as EffectiveDate,
           tab.col.value('@FirstName', 'VARCHAR(200)') as FirstName,
           tab.col.value('@LastName', 'VARCHAR(200)') as LastName,
           CASE 
              WHEN tab.col.value('@LicenseNumber','VARCHAR(50)') LIKE '%0000%' THEN NULL
              WHEN tab.col.value('@LicenseNumber','VARCHAR(50)') LIKE '%1111%' THEN NULL
              WHEN tab.col.value('@LicenseNumber','VARCHAR(50)') LIKE '%2222%' THEN NULL
              WHEN tab.col.value('@LicenseNumber','VARCHAR(50)') LIKE '%3333%' THEN NULL
              WHEN tab.col.value('@LicenseNumber','VARCHAR(50)') LIKE '%4444%' THEN NULL
              WHEN tab.col.value('@LicenseNumber','VARCHAR(50)') LIKE '%5555%' THEN NULL
              WHEN tab.col.value('@LicenseNumber','VARCHAR(50)') LIKE '%6666%' THEN NULL
              WHEN tab.col.value('@LicenseNumber','VARCHAR(50)') LIKE '%7777%' THEN NULL
              WHEN tab.col.value('@LicenseNumber','VARCHAR(50)') LIKE '%8888%' THEN NULL
              WHEN tab.col.value('@LicenseNumber','VARCHAR(50)') LIKE '%9999%' THEN NULL
              ELSE tab.col.value('@LicenseNumber','VARCHAR(50)')
           END as LicenseNumber,
           tab.col.value('@BirthDate','DATETIME') as BirthDate,
           REPLACE(tab.col.value('@Address1','VARCHAR(300)'), ' APT ',' #') as Address1,
           tab.col.value('@City','VARCHAR(300)') as City,
           tab.col.value('@State','VARCHAR(5)') as State,
           tab.col.value('@ZipCode','VARCHAR(10)') as Zip
FROM       @xml.nodes('//rows/datarow') as tab(col)

SELECT     B.PolicyNumber, 
           B.ProducerCode, 
           B.BridgeDate,
           B.EffectiveDate,
           H.current_policy,
           H.cancel_date,
           H.first_eff_date,
           H.display_address,
           H.city,
           H.state,
           H.zip
FROM       @Bridge B
LEFT JOIN  (
SELECT     P.policy_id,
           P.current_policy,
           CASE 
              WHEN A.pobox <> '' THEN 'PO BOX ' + REPLACE(A.pobox,'PO BOX ','')
              ELSE RTRIM(A.house_num + ' ' + A.street_name + ' ' + CASE
                                                                      WHEN A.apt_num = '' THEN ''
                                                                      ELSE '#' + A.apt_num
                                                                    END)
           END as display_address,
           A.pobox,
           A.house_num,
           A.street_name,
           A.apt_num,
           A.city,
           MAX(A.policyimage_num) as policimage_num, --this is just to limit the results to the most recent
           S.state,
           A.zip,
           P.first_eff_date,
           P.cancel_date
FROM       Diamond.dbo.Policy P WITH (NOLOCK)
LEFT JOIN  Diamond.dbo.Address A WITH (NOLOCK)
ON         P.policy_id = A.policy_id
AND        A.nameaddresssource_id = 3
LEFT JOIN  Diamond.dbo.State S WITH (NOLOCK)
ON         A.state_id = S.state_id
WHERE      A.state_id IS NOT NULL
AND        P.current_policy NOT IN (SELECT PolicyNumber FROM @Bridge)
GROUP BY   P.policy_id,
           P.current_policy,
           P.cancel_date,
           P.first_eff_date,
           A.pobox,
           A.house_num,
           A.street_name,
           A.apt_num,
           A.city,
           S.state,
           A.zip) AS H
ON         B.Address = H.display_address
AND        B.State = H.state
AND        B.City = H.city
AND        SUBSTRING(B.ZipCode,1,5) = SUBSTRING(H.Zip,1,5)
AND        B.PolicyNumber != H.current_policy
WHERE      H.current_policy IS NOT NULL

此查询自行运行,大约在 1:30 秒内完成。但是如果我在 WHERE 子句中添加以下内容

AND       B.EffectiveDate != H.first_eff_date

突然查询需要更长的时间才能返回结果。 (在我写这篇文章的时候,我们已经超过 15 分钟了,而且还在继续)我认为简单地有一个子句来清除一些额外的行不会产生如此剧烈的效果,但显然它确实如此。我如何解决它,我只是好奇是否有人对它为什么会产生这种效果有任何想法?

【问题讨论】:

  • 你看过这两个查询的实际执行计划了吗?这些应该告诉您该额外条件发生了什么 - 它对查询的执行有什么样的影响。也许这可以帮助您查明有助于再次加快速度的事情。
  • 您的(未索引的)表变量@Bridge 中有多少行?索引表可能是 (sorry) 键。
  • 您是否要根据AND A.nameaddresssource_id = 3 过滤掉记录。在使用 LEFT JOIN 和在连接条件中使用 AND 时,了解查询的行为很重要。如果您确定已正确放置连接,那么我们可以在不更改任何条件的情况下改进查询。检查此techpint.com/programming/how-sql-joins-works。您还可以在要放置连接的每个表中给出估计的行数。你能放一个查询的执行计划吗
  • 当您说WHERE H.&lt;anything&gt; 而不是将该子句添加到连接条件时,您针对H 的左外连接变成了内连接...那么为什么它根本是左外连接?
  • @Goblyn27 我不明白你的问题。你是说你“喜欢”左连接,你的意思是即使你试图获得一个内部连接?为什么?关于执行时间,我不确定,可能在您更改查询语法时会使用不同的计划,但不是真正的基本含义。 SQL Server 比较擅长检索你的意思,而不是你所说的,但它并不完美。如果您在进行内部连接,为什么不这样写?

标签: sql-server performance tsql


【解决方案1】:

没有动手我只能猜测这个,但这里有一些地方我认为你可以整理并可能节省运行时间。

1,您重复了确保政策编号不匹配所需的工作。选择你拥有的两个之一,而不是两个。我建议两者都试一下,看看哪个更快。

即这个:

AND        P.current_policy NOT IN (SELECT PolicyNumber FROM @Bridge)

会做同样的事情,你不需要两个。

AND        B.PolicyNumber != H.current_policy

2,值得尝试从您的子查询中删除所有分组 - 您实际上并没有使用 policimage_num 来做任何事情。那么为什么要分组呢?如果您担心从 Address 返回很多行,那么您可以在列集上使用 DISTINCT,这可能会更快。

3,A.state_id 是可以为空的值吗?如果不考虑尝试 INNER JOIN 到 Address 并删除 null 检查。

4,老实说,我根本没有看到该子查询的明显原因,这似乎使事情过于复杂。如果没有它,您是否不能简单地将表连接在一起(如果需要,再次使用 DISTINCT)?

换句话说,进行调整,我敢打赌,如果您尝试其中的一些想法,您可以将其降低到原始运行时间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-12
    • 2017-05-27
    • 2017-02-26
    • 1970-01-01
    • 2018-06-12
    • 1970-01-01
    相关资源
    最近更新 更多