需要帮助解决缓慢的查询

时间:2018-08-30 16:00:07

标签: sql sql-server query-optimization

我有一个查询,该查询将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;

两个表的AsOfDatePortfolioCode上都有索引。该查询需要7秒钟才能运行,我认为这太长了。

Execution Plan

我非常感谢您如何加快查询速度。

3 个答案:

答案 0 :(得分:1)

尝试这个。由于每个表中都有适当的索引,因此我们可以分别过滤它们,然后聚合而不是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;

尝试一下。

答案 1 :(得分:0)

这是您的查询(经过重新格式化):

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中使用别名使子查询合理。)

JOINWHERE条件的索引开始。我建议:

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

我将netamount放在第一个索引中,以便索引覆盖查询(即没有数据页查找)。

这可能会或可能不会大大提高性能。这取决于为GROUP BY处理多少数据。

答案 2 :(得分:0)

  • 我想知道HAVING SUM( ABS(ri.netamount)) != 0是否在这里足够早地启动,我猜想是由于查询计划中的Compute Scalar和Filter操作的顺序而导致的……我仍然希望对此更加明确。
  • 如Ivan Starostin所述,GROUP BY列上不需要AsOfDate,因为它是一个常数。
  • 由于优化程序似乎更喜欢使用合并联接,因此我们可以尝试通过添加覆盖索引来避免两种排序方式

例如

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