SQL Сервер: та же самая хранимая процедура прекрасно работает на 1 БД, но замедляется на 2 БД - PullRequest
0 голосов
/ 30 января 2020

У меня одна и та же хранимая процедура в 2 базах данных. Он отлично работает в первой, но занимает больше 10 секунд в другой базе данных. Я извлек это, чтобы запросить и есть подобная вещь. Вот мой запрос. Буду признателен за любую помощь экспертов.

SELECT     
    dbo.Installment.Id, dbo.Installment.InstallmentNo, 
    dbo.Installment.InstallmentOrder, dbo.Installment.Amount, 
    dbo.Installment.DueDate, dbo.Installment.AmountPaid, 
    dbo.Installment.PaidOn, dbo.Installment.PlotId, 
    dbo.Installment.SurchargePaid, dbo.Installment.surchargePaidOn, 
    dbo.Installment.PartialInstallmentId, dbo.Installment.Is_Lumpsum, 
    dbo.Plot.PlotNo, dbo.Plot.PhaseId, dbo.Plot.InstallmentPlanId, 
    dbo.InstallmentPlan.StartDate, dbo.InstallmentPlan.InstSurchargeDueMonth, 
    dbo.GetInstPlanDueDate(dbo.InstallmentPlan.StartDate, 
                           ISNULL(dbo.Installment.DueDate, GETDATE()), 
                           ISNULL(dbo.Installment.PaidOn, GETDATE()), 
                           dbo.InstallmentPlan.InstSurchargeDueMonth, 
                           dbo.Installment.InstallmentOrder, 
                           ISNULL(dbo.Installment.Is_Lumpsum, 0), 
                           InstallmentStartDate.DueDate, dbo.Installment.PlotId) AS Sutcharge_Start_From, 
    dbo.CalculateSurchargableDays(dbo.Installment.DueDate, 
                                  ISNULL(dbo.Installment.PaidOn, GETDATE()), 
                                  dbo.GetInstPlanDueDate(dbo.InstallmentPlan.StartDate, 
                                      ISNULL(dbo.Installment.DueDate, GETDATE()), 
                                      ISNULL(dbo.Installment.PaidOn, GETDATE()), 
                                      dbo.InstallmentPlan.InstSurchargeDueMonth, 
                                      dbo.Installment.InstallmentOrder, 
                                      ISNULL(dbo.Installment.Is_Lumpsum, 0), 
                                      InstallmentStartDate.DueDate, 
                                      dbo.Installment.PlotId), 
                        dbo.Installment.InstallmentOrder) AS days, 
                      case isnull(dbo.Installment.SurchargePaid,0) when 0 then
                  dbo.CalculateSurchargableDays(dbo.Installment.DueDate, ISNULL(dbo.Installment.PaidOn, GETDATE()), 
                  dbo.GetInstPlanDueDate(dbo.InstallmentPlan.StartDate, ISNULL(dbo.Installment.DueDate, GETDATE()), isnull(dbo.Installment.PaidOn,getdate()), dbo.InstallmentPlan.InstSurchargeDueMonth, dbo.Installment.InstallmentOrder, 
                  ISNULL(dbo.Installment.Is_Lumpsum, 0), InstallmentStartDate.DueDate, dbo.Installment.PlotId),dbo.Installment.InstallmentOrder) * (ISNULL(dbo.Installment.Amount, 0) * (dbo.InstallmentPlan.InstSurchargePercentage / 365 / 100))
                  else
                    isnull(dbo.Installment.Surcharge,0)
                  end
                  AS surcharge_calculated, case isnull(dbo.Installment.AmountPaid,0) when 0 then 0 else 0 end as Payment_Status,dbo.Installment.InstallmentOrder%6 as t


FROM           
    dbo.Installment 
INNER JOIN
    dbo.Plot ON dbo.Installment.PlotId = dbo.Plot.Id 
INNER JOIN
    dbo.InstallmentPlan ON dbo.Plot.InstallmentPlanId = dbo.InstallmentPlan.Id 
INNER JOIN
    (SELECT PlotId, MIN(DueDate) AS DueDate
     FROM dbo.Installment
     GROUP BY InstallmentOrder, PlotId
     HAVING (InstallmentOrder = 0)) AS InstallmentStartDate ON InstallmentStartDate.plotid = dbo.installment.plotid
WHERE     
    ((dbo.Plot.InstallmentPlanId > 0))

STATISTICS IO для того, кто отлично работает

(5089 row(s) affected)
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Installment'. Scan count 2, logical reads 109228, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Plot'. Scan count 1, logical reads 218, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'InstallmentPlan'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

и медленный:

(64577 row(s) affected)
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Installment'. Scan count 2, logical reads 2842959, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Plot'. Scan count 1, logical reads 272, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'InstallmentPlan'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

Мой фактический планы выполнения здесь

быстрый запрос

медленный запрос

1 Ответ

0 голосов
/ 30 января 2020

Проблема здесь в том, что у вас разные объемы данных между двумя вашими базами данных, и хотя запрос выполняется приемлемо для меньшей базы данных, он медленнее для большей.

Рассматривая два плана выполнения, мы можно увидеть, что оптимизатору приходится выполнять полное сканирование кластеризованных индексов для выполнения объединений, так как нет соответствующих индексов. Мне кажется, что главной проблемой является сканирование CI в таблице Installment, которое выполняется 2316 раз, в результате чего читается 5 583 876 строк (по сравнению с большей базой данных). Эта же операция читает 395 505 строк в запросе с меньшей базой данных. Ваш вывод STATISTICS IO говорит нам, что разница составляет 2 733 731 чтений. Это страницы размером 8 КБ, поэтому они занимают 2,6 ГБ, что довольно существенно и, вероятно, является причиной изменения времени выполнения.

Вы можете устранить это сканирование, создав некластеризованный индекс в таблице Installment. :

CREATE INDEX IX_Installment_InstallmentOrder ON Installment
(
    InstallmentOrder
)
INCLUDES
(
    DueDate,
    PlotId
)

Это должно уменьшить количество операций чтения и ускорить запрос.

Даже если это работает, я подозреваю, что в будущем у вас могут возникнуть другие проблемы, если ваш набор данных продолжит расти из-за к двум скалярным функциям в вашем операторе SELECT - GetInstPlanDueDate и CalculateSurchargableDays.

Скалярные функции - это построчная обработка, которая плохо масштабируется для больших наборов данных, поэтому вам может потребоваться удалить их в в будущем и замените подходом, основанным на множестве (табличная функция, JOIN, представление, производная таблица и т. д. c)

Сказав это, производительность скалярной функции была улучшена в SQL Server 2019, если функция inlinable

...