为啥我重构的 SQL 比原来的版本效率低?
Posted
技术标签:
【中文标题】为啥我重构的 SQL 比原来的版本效率低?【英文标题】:Why is my refactored SQL less efficient than the original version?为什么我重构的 SQL 比原来的版本效率低? 【发布时间】:2012-07-06 16:46:22 【问题描述】:我正在尝试优化存储过程,在查看了执行计划和执行时间后,我对结果感到惊讶。谁能解释一下。
我写的原始 SQL 有 2 个几乎相同的选择,然后我将它们放在一个 CTE 中,我尝试重构它,所以主要工作只完成一次,填充一个表变量,这样我就可以有 2 个较小的选择以我需要的方式过滤数据。 2 个 select 语句之间的唯一区别是在第一个 select 中传递给 TVF_GetChildGroups 的值 @AreaID 表示当前上下文的报告区域,而在第二个 select 中 @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 毫秒,运行时间 = 11 毫秒。 表“#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 毫秒,经过的时间 = 10 毫秒。 表“#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 毫秒,运行时间 = 199 毫秒。 表“#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】:我还会比较您的原始查询和修改后的查询之间的实际执行计划。
还要检查tblUploadedScorecards
、tblUploadedScoreCardResults
和tblUploadedScorecardHeaders
上的索引。将这些表中的数据缓存到 @PreHierarchyResults 后,您可能会失去第二个查询中连接列上某些覆盖索引的好处。
【讨论】:
以上是关于为啥我重构的 SQL 比原来的版本效率低?的主要内容,如果未能解决你的问题,请参考以下文章