Обновлен до Sql Server 2008. Теперь один запрос выполняется очень медленно - PullRequest
1 голос
/ 06 декабря 2009

В выходные я обновил SQL Server 2000 до 2008. Теперь один запрос выполняется очень медленно (> 30 секунд для примерно 50 строк).

Запрос:

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 минут в зависимости от параметров. Добавление индексов в соответствии с предложением плана выполнения и dm_db_missing_index_details сократило время выполнения до 4 секунд Добавление хэш-подсказки уменьшило время выполнения до 2 секунд.

Это был сложный вопрос, решить, какой из ответов принять, большинство ответов оказали некоторую помощь.

Ответы [ 4 ]

2 голосов
/ 07 декабря 2009

Сравните actual execution plan на обеих машинах. Графическая версия, вероятно, наиболее полезна; Вы можете сравнить деревья и проверить, какая стрелка действительно велика на сервере 2008 года.

Для размещения в переполнении стека извлекайте план в текстовой форме, например:

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 Profiler и следовать указаниям индекса, которые он генерирует. Также проверьте вкладку messages в SSMS; иногда он включает предложения по указателю.

1 голос
/ 07 декабря 2009

Является ли запрос медленным только в первый раз или каждый раз? Если первое, это может быть проблема с кешированием.

В новой системе используется та же дисковая подсистема, что и в старой? Если нет, проблема может быть связана со скоростью дисков, которые вы используете для файлов данных.

Обновили ли вы БД из режима совместимости 90 до 100 или сохранили ее на уровне 90?

Вы тоже перенесли все данные со старой системы? Если нет, возможно, статистика другая, что приводит к другому (и более медленному) плану запросов.

Вы пытались использовать отсутствующую функцию индекса в SQL 2008?

SELECT * FROM sys.dm_db_missing_index_details
0 голосов
/ 08 декабря 2009

У меня была очень похожая проблема, возникавшая с 2000 по 2005 год. У нас был просмотр таблицы из 1 миллиона строк (с самостоятельными объединениями и т. Д.), И запрос выполнялся более трех часов (мы никогда не позволяли ему завершиться, поэтому не знаю, вернется ли он когда-нибудь). Кажется, моя проблема напрямую связана с количеством «вложенных циклов» в таблице. Я вижу довольно много в вашем плане выполнения:

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 Hint : Hash на всех моих проблемных объединениях, и это уменьшило время запроса до более управляемых 30-45 минут.

Я бы тоже хотел найти основную причину, но это основная работа.

0 голосов
/ 08 декабря 2009

Ничто особенно не выделяется как проблемное. Хотя я отмечаю несколько мелких проблем, о которых упомяну.

Был ли план запроса для того же запроса? Я вижу ссылку на 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 в вашем запросе; это кажется странным, возможно, излишним. Скорее всего, это просто усложняет работу оптимизатора запросов, заставляя его учитывать то, что на самом деле является избыточным.

План запроса показывает все объединения с использованием «вложенных циклов». Обычно это нормально, если объем данных при каждом соединении не слишком велик. (Вы можете проверить это на своем графическом плане). По сути, план, похоже, затрагивает соответствующие индексы для всех объединений. Сложно быть уверенным, что вы не видите точно, что это за индексы, и не видите объёмов данных.

В качестве подсказки, в следующий раз, когда вы столкнетесь с этой проблемой, посмотрите на графический план запроса и посмотрите, какие узлы очень дороги. Он поможет вам проверить индексы (для создания или перестройки).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...