Нужна помощь в устранении неполадок медленного запроса - PullRequest
0 голосов
/ 30 августа 2018

У меня есть запрос, который объединяет 2 таблицы ReconCollaterlExternal (1194994 строки) и ReconCollateralInternal (888060 строк).

Так что это действительно не большие таблицы, и вот запрос:

DECLARE @asofdate DATE = '2018-08-29';
DECLARE @threshold INT = 25

SELECT  A.* FROM (
        SELECT ri.AsOfDate, ri.Portfoliocode, SUM( ABS(ri.netamount)) SumAbsEmcMtm, SUM( ABS(re.netamount)) SumAbsBrokerMtm, 
        100*(SUM( ABS(ri.netamount))- SUM( ABS(re.netamount)))/SUM( ABS(ri.netamount)) PctMtmBreak 
        FROM ReconCollateralExternal ri
        INNER JOIN ReconCollateralInternal re ON re.portfoliocode = ri.portfoliocode AND  re.AsOfDate = ri.AsOfDate 
        WHERE ri.asofdate = @asofdate GROUP BY ri.portfoliocode , ri.AsOfDate  HAVING SUM( ABS(ri.netamount)) != 0 
         ) A
WHERE ABS(A.PctMtmBreak) >= @threshold ORDER BY ABS(A.PctMtmBreak) DESC;

В обеих таблицах есть индексы AsOfDate, PortfolioCode. Выполнение запроса занимает 7 секунд, что, на мой взгляд, слишком долго.

Execution Plan

Я ценю любую помощь, как ускорить запрос.

Ответы [ 3 ]

0 голосов
/ 30 августа 2018

Попробуйте это. Поскольку в каждой таблице есть соответствующий индекс, мы можем отфильтровать их отдельно, затем агрегировать вместо sorg + join, а затем объединить агрегированные значения.

DECLARE @asofdate DATE = '2018-08-29';
DECLARE @threshold INT = 25

SELECT  
  @asofdate AsOfDate, 
  A.Portfoliocode,
  A.SumAbsEmcMtm,
  A.SumAbsBrokerMtm,
  A.PctMtmBreak 
FROM 
  (
     SELECT
        ri.Portfoliocode, ri.SumAbsEmcMtm, re.SumAbsBrokerMtm,
        100*(ri.SumAbsEmcMtm- re.SumAbsBrokerMtm)/ri.SumAbsEmcMtm PctMtmBreak 
     FROM
     (
        SELECT
           ri.portfoliocode,
           SUM(ABS(ri.netamount)) SumAbsEmcMtm
        FROM ReconCollateralExternal ri
        WHERE ri.asofdate = @asofdate 
        GROUP BY ri.portfoliocode
        HAVING SUM( ABS(ri.netamount)) != 0 
     ) ri
     INNER JOIN 
     (
        SELECT
           re.portfoliocode,
           SUM(ABS(re.netamount)) SumAbsBrokerMtm
        FROM ReconCollateralInternal re
        WHERE re.asofdate = @asofdate
        GROUP BY re.portfoliocode
     ) re ON re.portfoliocode = ri.portfoliocode
  ) A
WHERE ABS(A.PctMtmBreak) >= @threshold 
ORDER BY ABS(A.PctMtmBreak) DESC;

попробуй.

0 голосов
/ 05 сентября 2018
  • Интересно, если HAVING SUM( ABS(ri.netamount)) != 0 появится достаточно рано, я предполагаю, что это происходит из-за порядка операций Compute Scalar и Filter, выполняемых в плане запроса ... все же, я бы предпочел быть более явным об этом.
  • Как уже упоминал Иван Старостин, нет необходимости GROUP BY в столбце AsOfDate, потому что это константа.
  • Поскольку оптимизатор предпочитает использовать объединение слиянием, мы могли бы попытаться избежать двух сортировок, добавив индекс покрытия

1012 *, например *

CREATE INDEX idx_test ON ReconCollateralExternal (AsOfDate, PortofolioCode) INCLUDE (NetAmount)
CREATE INDEX idx_test ON ReconCollateralInternal (AsOfDate, PortofolioCode) INCLUDE (NetAmount)

Имейте в виду, что такого понятия, как бесплатный обед, не существует: индексы могут сделать запрос (немного) быстрее (?), Но это окажет (небольшое) влияние на производительность операций вставки / обновления / удаления на столе в другом месте!

Тогда запрос будет выглядеть примерно так:

DECLARE @asofdate DATE = '2018-08-29';
DECLARE @threshold INT = 25


SELECT  Portfoliocode,
        AsOfDate = @asofdate,
        SumAbsEmcMtm,
        SumAbsBrokerMtm,
        100 * (SumAbsEmcMtm - SumAbsBrokerMtm) / SumAbsEmcMtm PctMtmBreak 

  FROM (SELECT ri.Portfoliocode, 
               SUM( ABS(ri.NetAmount)) SumAbsEmcMtm, 
               SUM( ABS(re.NetAmount)) SumAbsBrokerMtm
               -- 100 * (SUM (ABS(ri.NetAmount)) - SUM( ABS(re.netamount))) / SUM( ABS(ri.netamount)) PctMtmBreak 
          FROM ReconCollateralExternal ri
          JOIN ReconCollateralInternal re 
            ON re.PortfolioCode = ri.PortfolioCode 
           AND re.AsOfDate = @asofdate  -- ri.AsOfDate 
          WHERE ri.asofdate = @asofdate 
          GROUP BY ri.PortfolioCode 
         HAVING SUM( ABS(ri.NetAmount)) != 0 
         ) A
WHERE ABS(100 * (SumAbsEmcMtm - SumAbsBrokerMtm) / SumAbsEmcMtm ) >= @threshold 
ORDER BY ABS(100 * (SumAbsEmcMtm - SumAbsBrokerMtm) / SumAbsEmcMtm ) DESC; 

PS: имейте в виду, что при развертывании этого кода на чувствительном к регистру сервере он не будет компилироваться, например, PortofolioCode! = Portofoliocode

0 голосов
/ 30 августа 2018

Это ваш запрос (немного переформатированный):

SELECT ri.AsOfDate, ri.Portfoliocode, SUM( ABS(ri.netamount)) as SumAbsEmcMtm, SUM( ABS(re.netamount)) as SumAbsBrokerMtm, 
        100*(SUM( ABS(ri.netamount))- SUM( ABS(re.netamount)))/SUM( ABS(ri.netamount)) as PctMtmBreak 
FROM ReconCollateralExternal ri INNER JOIN
     ReconCollateralInternal re 
     ON re.portfoliocode = ri.portfoliocode AND re.AsOfDate = ri.AsOfDate 
WHERE ri.asofdate = @asofdate
GROUP BY ri.portfoliocode, ri.AsOfDate 
HAVING SUM( ABS(ri.netamount)) <> 0 AND
       100*(SUM( ABS(ri.netamount))- SUM( ABS(re.netamount)))/SUM( ABS(ri.netamount)) >= @threshold
ORDER BY PctMtmBreak DESC;

(Подзапрос не влияет на производительность. Я просто удалил его, потому что мне проще визуализировать обработку. Использование псевдонима в вашем внешнем HAVING делает подзапрос разумным.)

Начните с индексов условий JOIN s и WHERE. Я бы порекомендовал:

  • ReconCollateralExternal(asofdate, portfoliocode, netamount)
  • ReconCollateralInternal(portfoliocode, asofdate)

Я помещаю netamount в первый индекс только для того, чтобы индекс покрывал запрос (т. Е. Нет поиска страниц данных).

Это может или не может быть большим увеличением производительности. Это зависит от того, сколько данных обрабатывается для GROUP BY.

...