升级到Sql Server 2008.现在一个查询运行速度很慢

时间:2009-12-06 20:48:36

标签: sql-server

我在周末从sql server 2000升级到2008。现在,一个查询运行速度非常慢(大约50行,大约30秒)。

查询是:

SELECT     TOP 200 AccData.SurName + ', ' + AccData.FirstNames AS Name, 
    DATEDIFF(day, COALESCE (AccData.DateReceived, AccData.DateOpened,
    AccData.InjuryDate), 
    GETDATE()) AS Duration, AccData.M46No, Clients.ClientName, 
    AccData.HomePhone, AccData.WorkPhone, AccData.InjuryDate, 
    AccData.ClaimID, 
    luClaimStatus.Meaning AS Status, AccData.Claim, 
    vw_LastMedCert.Fitness, vw_LastMedCert.UntilDate
FROM         AccData INNER JOIN
    Clients ON AccData.ClientID = Clients.ID 
    INNER JOIN
        luClaimStatus ON AccData.ClaimStatus = luClaimStatus.ClaimStatus 
    LEFT OUTER JOIN
        vw_LastMedCert ON AccData.Claim = vw_LastMedCert.Claim
WHERE AccData.ClientID>1 and CaseManagerId = :CaseManagerID 
    and (DateClosed is null or  AccData.ClaimStatus ='R')
order by Surname, FirstNames

问题与LastMedCert

有关
ALTER VIEW [dbo].[vw_LastMedCert] WITH SCHEMABINDING
AS
SELECT     Claim, ClaimId, ReferralID, FromDate, UntilDate, Fitness, DateSeen, 
    DateEntered, PeriodFor
FROM         dbo.Med_cert
WHERE     (ReferralID IN
                      (SELECT     MAX(ReferralID) AS MaxOfReferralID
                        FROM          dbo.Med_cert AS Med_cert_1
                        WHERE      (Fitness IS NOT NULL)
                        GROUP BY Claim))

有什么想法吗?我重建了索引并更新了统计信息

执行计划是:

|--Compute Scalar(DEFINE:([Expr1020]=datediff(day,[Expr1024],getdate())))
   |--Nested Loops(Left Outer Join, OUTER REFERENCES:([CmsDB].[dbo].[AccData].[Claim]))
        |--Nested Loops(Inner Join, OUTER REFERENCES:([CmsDB].[dbo].[AccData].[ClientID]))
        |    |--Nested Loops(Inner Join, OUTER REFERENCES:([CmsDB].[dbo].[AccData].[ClaimStatus]))
        |    |    |--Compute Scalar(DEFINE:([Expr1019]=((([CmsDB].[dbo].[AccData].[SurName]+', ')+[CmsDB].[dbo].[AccData].[FirstNames])+' ')+CASE WHEN [CmsDB].[dbo].[AccData].[MiddleNames] IS NOT NULL THEN [CmsDB].[dbo].[AccData].[MiddleNames] ELSE '' END, [Expr1024]=CASE WHEN [CmsDB].[dbo].[AccData].[DateReceived] IS NOT NULL THEN [CmsDB].[dbo].[AccData].[DateReceived] ELSE CASE WHEN [CmsDB].[dbo].[AccData].[DateOpened] IS NOT NULL THEN [CmsDB].[dbo].[AccData].[DateOpened] ELSE [CmsDB].[dbo].[AccData].[InjuryDate] END END))
        |    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([Uniq1002], [CmsDB].[dbo].[AccData].[Claim], [CmsDB].[dbo].[AccData].[SurName], [CmsDB].[dbo].[AccData].[FirstNames], [Expr1027]) WITH ORDERED PREFETCH)
        |    |    |         |--Index Seek(OBJECT:([CmsDB].[dbo].[AccData].[IX_AccData_ByCaseManagerId]), SEEK:([CmsDB].[dbo].[AccData].[CaseManagerID]=(100346)) ORDERED FORWARD)
        |    |    |         |--Clustered Index Seek(OBJECT:([CmsDB].[dbo].[AccData].[byName]), SEEK:([CmsDB].[dbo].[AccData].[SurName]=[CmsDB].[dbo].[AccData].[SurName] AND [CmsDB].[dbo].[AccData].[FirstNames]=[CmsDB].[dbo].[AccData].[FirstNames] AND [CmsDB].[dbo].[AccData].[Claim]=[CmsDB].[dbo].[AccData].[Claim] AND [Uniq1002]=[Uniq1002]),  WHERE:([CmsDB].[dbo].[AccData].[ClientID]>(1) AND ([CmsDB].[dbo].[AccData].[DateClosed] IS NULL OR [CmsDB].[dbo].[AccData].[ClaimStatus]='R')) LOOKUP ORDERED FORWARD)
        |    |    |--Clustered Index Seek(OBJECT:([CmsDB].[dbo].[luClaimStatus].[PK_luClaimStatus_1__172]), SEEK:([CmsDB].[dbo].[luClaimStatus].[ClaimStatus]=[CmsDB].[dbo].[AccData].[ClaimStatus]) ORDERED FORWARD)
        |    |--Index Seek(OBJECT:([CmsDB].[dbo].[Clients].[PK_Clients_2__13]), SEEK:([CmsDB].[dbo].[Clients].[ID]=[CmsDB].[dbo].[AccData].[ClientID]),  WHERE:([CmsDB].[dbo].[Clients].[ID]>(1)) ORDERED FORWARD)
        |--Nested Loops(Inner Join, WHERE:([Expr1018]=[CmsDB].[dbo].[Med_cert].[ReferralID]))
             |--Clustered Index Seek(OBJECT:([CmsDB].[dbo].[Med_cert].[byClaim]), SEEK:([CmsDB].[dbo].[Med_cert].[Claim]=[CmsDB].[dbo].[AccData].[Claim]) ORDERED FORWARD)
             |--Table Spool
                  |--Stream Aggregate(GROUP BY:([CmsDB].[dbo].[Med_cert].[Claim]) DEFINE:([Expr1018]=MAX([CmsDB].[dbo].[Med_cert].[ReferralID])))
                       |--Clustered Index Scan(OBJECT:([CmsDB].[dbo].[Med_cert].[byClaim]),  WHERE:([CmsDB].[dbo].[Med_cert].[Fitness] IS NOT NULL) ORDERED FORWARD)

我通过重写初始查询解决了这个问题。它现在运行大约1秒钟,但我仍然想知道出了什么问题,所以如果它再次出现我可以解决它。

摘要 初始查询运行时间约为2分钟,具体取决于参数。按照exeqution计划和dm_db_missing_index_details的建议添加索引可将运行时间减少到大约4秒。 添加哈希提示可将运行时间缩短至2秒。

这是一个艰难的决定,决定接受哪个答案,大多数答案提供了一些帮助。

4 个答案:

答案 0 :(得分:2)

比较两台机器上的actual execution plan。图形版本可能是最有用的;您可以比较树木,并检查2008服务器上哪个箭头非常大。

要在Stack Overflow上发布,请以文本形式检索计划,如:

set showplan_text on
go
<your query>

编辑:执行计划提到了聚集索引扫描:

|--Clustered Index Scan(OBJECT:([CmsDB].[dbo].[Med_cert].[byClaim]),  
WHERE:([CmsDB].[dbo].[Med_cert].[Fitness] IS NOT NULL) ORDERED FORWARD)

我会在med_cert(fitness,claim,ReferralID)上使用索引来尝试此操作。更好的是,您可以在SQL事件探查器中运行它并遵循它生成的索引建议。另请查看SSMS中的messages标签;它有时包括索引建议。

答案 1 :(得分:1)

查询只是第一次或每次都慢吗?如果是前者,那可能是一个缓存问题。

新系统是否使用与旧系统相同的磁盘子系统?如果没有,问题可能与您用于数据文件的磁盘速度有关。

您是否将数据库从兼容模式90升级到100,或者您将其保持在90?

您是否也从旧系统中提取了所有数据?如果不是,可能统计数据不同,导致查询计划不同(且速度较慢)。

您是否尝试过使用SQL 2008中缺少的索引功能?

SELECT * FROM sys.dm_db_missing_index_details

答案 2 :(得分:0)

没有什么特别突出的问题。虽然我注意到一些小问题,但我会提到。

同一查询的查询计划是什么?我在计划中看到对AccData.MiddleNames的引用,但在查询中没有。

我确实有一些观察......

该计划表明您在AccData上的聚集索引是SurName,FirstName,Claim。这不理想;你更喜欢PK或者为你的聚集索引很好地组合的数据。如果您不使用PK,则必须执行如下图所示的“书签查找”,以通过聚集索引获取完整记录。如果优化器看到/预测这种情况发生得太多,它可能更喜欢进行表扫描并可能对数据进行排序。

        |    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([Uniq1002], [CmsDB].[dbo].[AccData].[Claim], [CmsDB].[dbo].[AccData].[SurName], [CmsDB].[dbo].[AccData].[FirstNames], [Expr1027]) WITH ORDERED PREFETCH)
        |    |    |         |--Index Seek(OBJECT:([CmsDB].[dbo].[AccData].[IX_AccData_ByCaseManagerId]), SEEK:([CmsDB].[dbo].[AccData].[CaseManagerID]=(100346)) ORDERED FORWARD)
        |    |    |         |--Clustered Index Seek(OBJECT:([CmsDB].[dbo].[AccData].[byName]), SEEK:([CmsDB].[dbo].[AccData].[SurName]=[CmsDB].[dbo].[AccData].[SurName] AND [CmsDB].[dbo].[AccData].[FirstNames]=[CmsDB].[dbo].[AccData].[FirstNames] AND [CmsDB].[dbo].[AccData].[Claim]=[CmsDB].[dbo].[AccData].[Claim] AND [Uniq1002]=[Uniq1002]),  WHERE:([CmsDB].[dbo].[AccData].[ClientID]>(1) AND ([CmsDB].[dbo].[AccData].[DateClosed] IS NULL OR [CmsDB].[dbo].[AccData].[ClaimStatus]='R')) LOOKUP ORDERED FORWARD)

我在您的查询中注明了WHERE ClientID>1;这似乎很奇怪,也许是多余的。通过强迫它考虑实际上是多余的东西,它更有可能使查询优化器的工作变得复杂。

查询计划使用“嵌套循环”显示所有连接。如果每次连接的数据量不是太多,这通常没问题。 (您可以在图形计划中查看)。基本上,该计划似乎在所有连接上都达到了适当的索引。确保没有确切地看到这些索引是什么,并且没有看到涉及的数据量,这很棘手。

作为提示,下次遇到此问题时,请查看图形查询计划,并查看哪些节点非常昂贵。它将指导您检查索引的位置(用于创建或重建)。

答案 3 :(得分:0)

我有一个非常类似的问题从2000年到2005年。我们查看了100万行的表(有自联接等),查询运行了三个多小时(我们永远不会让它完成,所以我们不知道它是否会回归)。我的问题似乎与表中“嵌套循环”的数量直接相关。我在你的执行计划中看到了不少:

Nested Loops(Left Outer Join, OUTER REFERENCES:([CmsDB].[dbo].[AccData].[Claim]))
    |--Nested Loops(Inner Join, OUTER REFERENCES:([CmsDB].[dbo].[AccData].[ClientID]))
    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([CmsDB].[dbo].[AccData].[ClaimStatus]))

我在所有有问题的联接上使用了Query HintHash,并且将查询的时间缩短到了30-45分钟。

我也很想找到根本原因,但这是一个基本的工作。