需要帮助来解决慢查询问题

问题描述 投票:1回答:3

我有一个查询连接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

我感谢任何帮助如何加快查询速度。

sql sql-server query-optimization
3个回答
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;

试试看。


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

JOINs和WHERE条件的索引开始。我建议:

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

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

这可能会或可能不会带来很大的性能提升。这取决于GROUP BY处理的数据量。


0
投票
  • 我想知道HAVING SUM( ABS(ri.netamount)) != 0是否足够早在这里踢,我猜它确实是由于计算标量和过滤器操作在查询计划中的顺序...但是,我宁愿更明确地说明它。
  • 正如Ivan Starostin已经提到的那样,GROUP BY列上没有必要使用AsOfDate,因为它是一个常数。
  • 由于优化器似乎更喜欢使用Merge Join,我们可以尝试通过添加覆盖索引来避免这两种情况

EG

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:请记住,当您在区分大小写的服务器上部署此代码时,它将无法编译,例如Portofolio Code!= portofolio代码

© www.soinside.com 2019 - 2024. All rights reserved.