我想優化一個存儲過程,並在查看執行計劃和執行時間後,我對結果感到驚訝。任何人都可以解釋。爲什麼我的重構SQL比原始版本效率低?
原來的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 row (s)affected)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個表格,而在第二個版本中這些表格只觸發一次。