为什么我的重构SQL效率低于原始版本?

时间:2012-07-06 16:46:22

标签: tsql optimization

我正在尝试优化存储过程,在查看执行计划和执行时间后,我对结果感到惊讶。任何人都可以解释它们。

我编写的原始SQL有2个几乎完全相同的选择然后我在CTE中一起拉,我试着重构那么主要的工作做了一次,填充表变量所以我可以有2个较小的选择,按我需要的方式过滤数据。 2个select语句之间的唯一区别是在第一个选择中传递给TVF_GetChildGroups的值@AreaID表示当前上下文的报告区域,在第二个选择中@RootReportLevelID只是所有区域。因此,它是用户上下文的同行,目的是允许比较您的分数和其他人的平均值。

DECLARE @Scores TABLE
(
  ShortName VARCHAR(50) ,
  PCTMax INT ,
  PCTAvg INT ,
  PCTMin INT ,
  ALLAvg INT ,
  AllMax INT ,
  AllMin INT
)
INSERT  INTO @Scores
    SELECT  T2.ShortName ,
            MAX(T1.MeridianScore) AS PCTMax ,
            CAST(( CAST(SUM(T1.contribution) AS DECIMAL) / CAST(SUM(T1.maxvalue) AS DECIMAL) * 100 ) AS DECIMAL(5, 2)) AS PCTAvg ,
            MIN(T1.MeridianScore) AS PCTMin ,
            CAST(( CAST(SUM(T2.contribution) AS DECIMAL) / CAST(SUM(T2.maxvalue) AS DECIMAL) * 100 ) AS DECIMAL(5, 2)) AS AllAvg ,
            MAX(T2.MeridianScore) AS AllUpperScore ,
            MIN(T2.MeridianScore) AS AllLowerScore
    FROM    ( SELECT    US.PKID ,
                        PT.ShortName ,
                        CAST(( CAST(SUM(RES.contribution) AS DECIMAL) / CAST(SUM(RES.maxvalue) AS DECIMAL) * 100 ) AS DECIMAL(5, 2)) AS MeridianScore ,
                        SUM(Contribution) AS Contribution ,
                        SUM(MaxValue) AS MaxValue
              FROM      tblUploadedScorecards AS US
                        INNER JOIN tblUploadedScoreCardResults AS RES ON US.PKID = RES.FKUploadedScoreCardID
                        INNER JOIN tblUploadedScorecardHeaders AS USH ON USH.FKUploadedScorecardID = US.PKID
                        INNER JOIN @ProviderTable AS PT ON USH.HeaderValue = PT.FullName
                        INNER JOIN TVF_GetChildGroups(@AreaID) AS TGCG ON TGCG.GroupName = US.Branch
              WHERE     US.FKScoreCardID = 185
                        AND reviewed = 1
                        AND ShopDate BETWEEN @StartDate AND @EndDate
                        AND RES.Rating <> 'I'
                        AND USH.FKScorecardHeaderID = 71
              GROUP BY  US.PKID ,
                        PT.ShortName ) AS T1
            RIGHT JOIN ( SELECT US.PKID ,
                                PT.ShortName ,
                                CAST(( CAST(SUM(RES.contribution) AS DECIMAL) / CAST(SUM(RES.maxvalue) AS DECIMAL) * 100 ) AS DECIMAL(5, 2)) AS MeridianScore ,
                                SUM(Contribution) AS Contribution ,
                                SUM(MaxValue) AS MaxValue
                         FROM   tblUploadedScorecards AS US
                                INNER JOIN tblUploadedScoreCardResults AS RES ON US.PKID = RES.FKUploadedScoreCardID
                                INNER JOIN tblUploadedScorecardHeaders AS USH ON USH.FKUploadedScorecardID = US.PKID
                                INNER JOIN @ProviderTable AS PT ON USH.HeaderValue = PT.FullName
                                INNER JOIN TVF_GetChildGroups(@RootReportLevelID) AS TGCG ON TGCG.GroupName = US.Branch
                         WHERE  US.FKScoreCardID = 185
                                AND reviewed = 1
                                AND ShopDate BETWEEN @StartDate AND @EndDate
                                AND RES.Rating <> 'I'
                                AND USH.FKScorecardHeaderID = 71
                         GROUP BY US.PKID ,
                                PT.ShortName ) AS T2 ON T1.ShortName = T2.ShortName
    GROUP BY T2.ShortName

所以我将共性重构为一个表变量,以后我可以将连接应用到TVF,希望不需要这么多地打到表格并最终得到这个:

DECLARE @PreHierarchyResults TABLE 
(
PKID INT ,
Branch VARCHAR(150) ,
ShortName VARCHAR(50) ,
Contribution DECIMAL(20,3),
MaxValue DECIMAL(20,3)
)

INSERT INTO @PreHierarchyResults
    ( PKID ,
      Branch ,
      ShortName ,
      Contribution ,
      MaxValue )
SELECT  US.PKID ,
    US.Branch ,
    PT.ShortName ,
    Contribution AS Contribution ,
    MaxValue AS MaxValue
FROM    tblUploadedScorecards AS US
    INNER JOIN tblUploadedScoreCardResults AS RES ON US.PKID = RES.FKUploadedScoreCardID
    INNER JOIN tblUploadedScorecardHeaders AS USH ON USH.FKUploadedScorecardID = US.PKID
    INNER JOIN @ProviderTable AS PT ON USH.HeaderValue = PT.FullName
WHERE   US.FKScoreCardID = 185
    AND reviewed = 1
    AND ShopDate BETWEEN @StartDate AND @EndDate
    AND RES.Rating <> 'I'
    AND USH.FKScorecardHeaderID = 71


INSERT  INTO @Scores
    SELECT  T2.ShortName ,
            MAX(T1.MeridianScore) AS PCTMax ,
            CAST(( CAST(SUM(T1.contribution) AS DECIMAL) / CAST(SUM(T1.maxvalue) AS DECIMAL) * 100 ) AS DECIMAL(5, 2)) AS PCTAvg ,
            MIN(T1.MeridianScore) AS PCTMin ,
            CAST(( CAST(SUM(T2.contribution) AS DECIMAL) / CAST(SUM(T2.maxvalue) AS DECIMAL) * 100 ) AS DECIMAL(5, 2)) AS AllAvg ,
            MAX(T2.MeridianScore) AS AllUpperScore ,
            MIN(T2.MeridianScore) AS AllLowerScore
    FROM    ( SELECT    PHR.PKID ,
                        ShortName ,
                        CAST(( CAST(SUM(contribution) AS DECIMAL) / CAST(SUM(maxvalue) AS DECIMAL) * 100 ) AS DECIMAL(5, 2)) AS MeridianScore ,
                        SUM(Contribution) AS Contribution ,
                        SUM(MaxValue) AS MaxValue
              FROM      @PreHierarchyResults AS PHR 
                        INNER JOIN TVF_GetChildGroups(@AreaID) AS TGCG ON TGCG.GroupName = PHR.Branch 
              GROUP BY PHR.PKID , ShortName) AS T1
            RIGHT JOIN ( SELECT PHR2.PKID ,
                                ShortName ,
                                CAST(( CAST(SUM(Contribution) AS DECIMAL) / CAST(SUM(Maxvalue) AS DECIMAL) * 100 ) AS DECIMAL(5, 2)) AS MeridianScore ,
                                SUM(Contribution) AS Contribution ,
                                SUM(MaxValue) AS MaxValue
                         FROM   @PreHierarchyResults AS PHR2
                                INNER JOIN TVF_GetChildGroups(@RootReportLevelID) AS TGCG ON TGCG.GroupName = PHR2.Branch 
                         GROUP BY PHR2.PKID , ShortName) AS T2 ON T1.ShortName = T2.ShortName
    GROUP BY T2.ShortName

对于原始SQL,我得到执行时间:

  

SQL Server执行时间:
     CPU时间= 16 ms,经过时间= 11 ms   表'#012F94F2'。扫描计数0,逻辑读取3,物理读取0,预读读取0,lob逻辑读取0,lob物理读取0,lob预读读取0   表'tblUploadedScoreCardResults'。扫描计数4456,逻辑读取13943,物理读取40,预读读取0,lob逻辑读取0,lob物理读取0,lob预读读取0。   表'工作台'。扫描计数6,逻辑读取29800,物理读取0,预读读取0,lob逻辑读取0,lob物理读取0,lob预读读取0   表'tblUserGroups'。扫描计数2,逻辑读取118,物理读取1,预读读取0,lob逻辑读取0,lob物理读取0,lob预读读取0   表'tblUploadedScorecards'。扫描计数0,逻辑读取29496,物理读取8,预读读取0,lob逻辑读取0,lob物理读取0,lob预读读取0   表'tblUploadedScorecardHeaders'。扫描计数192,逻辑读取746,物理读取5,预读读取0,lob逻辑读取0,lob物理读取0,lob预读读取0   表'#7D5F040E'。扫描计数186,逻辑读取372,物理读取0,预读读取0,lob逻辑读取0,lob物理读取0,lob预读读取0.

在重构之后我得到了:

  

SQL Server执行时间:
     CPU时间= 0 ms,经过时间= 10 ms   表'#48563EF2'。扫描计数0,逻辑读取5106,物理读取0,预读读取0,lob逻辑读取0,lob物理读取0,lob预读读取0   表'tblUploadedScoreCardResults'。扫描计数185,逻辑读取614,物理读取41,预读读取0,lob逻辑读取0,lob物理读取0,lob预读读取0   表'tblUploadedScorecards'。扫描计数0,逻辑读取370,物理读取8,预读读取0,lob逻辑读取0,lob物理读取0,lob预读读取0   表'tblUploadedScorecardHeaders'。扫描计数7,逻辑读取23,物理读取5,预读读取0,lob逻辑读取0,lob物理读取0,lob预读读取0   表'#439189D5'。扫描计数1,逻辑读取2,物理读取0,预读取读取0,lob逻辑读取0,lob物理读取0,lob预读读取0   (5059行受影响)
  (1行受影响)

     

SQL Server执行时间:
     CPU时间= 16 ms,经过时间= 199 ms   表'#47621AB9'。扫描计数0,逻辑读取3,物理读取0,预读读取0,lob逻辑读取0,lob物理读取0,lob预读读取0   表'工作台'。扫描计数114,逻辑读取1770,物理读取0,预读取读取0,lob逻辑读取0,lob物理读取0,lob预读读取0   表'tblUserGroups'。扫描计数112,逻辑读取998,物理读取1,预读读取0,lob逻辑读取0,lob物理读取0,lob预读读取0   表'#48563EF2'。扫描计数112,逻辑读取5376,物理读取0,预读读取0,lob逻辑读取0,lob物理读取0,lob预读读取0.

看看输出我认为很明显第一个版本效率更高,但我只是不明白为什么它应该是这样的,因为它似乎两次击中4个表而在第二个版本中这些表只被击中一次。

1 个答案:

答案 0 :(得分:0)

我还会比较原始查询和修订查询之间的实际执行计划。

同时检查tblUploadedScorecardstblUploadedScoreCardResultstblUploadedScorecardHeaders上的索引。将这些表中的数据缓存到@PreHierarchyResults后,您可能会失去第二个查询中连接列上某些覆盖索引的好处。