2014-01-21 58 views
0

我有一個存儲過程發出類似於下面的查詢(僞tsql)的查詢。SQL Server組合多個表時的執行計劃

將多個ParentIds作爲參數(csv)傳入,解析並插入表變量@i。對於每個ParentId通過,我們查找StorageTable並將其包含在@i中。現在,根據StorageTable列的值,我們需要從適當的表格(Table1,Table2Table3)提取數據ParentId。不存在跨多個表重複的機會 - 因此UNION ALL

當我檢查實際的執行計劃時,我發現我的大部分成本/子樹成本(超過一半)花在StorageTable上,甚至沒有作爲輸入提供。

例如,如果我包含StorageTable = 'Table1',表2的索引掃描將顯示在執行計劃中的成本很高。

正如我所料,STATISTICS IO沒有顯示任何對錶2的讀取,但根據實際的執行計劃,數據訪問點看起來很昂貴。

在我看來,如果某個特定的StorageTable不存在,那麼@i的內部連接將返回一個空的結果集並「短路」任何額外的工作,不是?

什麼是解決方案?

DECLARE @i AS TABLE 
(
    ParentId INT, 
    StorageTable VARCHAR(10) 
) 

INSERT INTO @i... 
INSERT INTO @i... 
INSERT INTO @i... 

SELECT Col1, Col2, Col3 
FROM dbo.Table1 AS T1 
INNER JOIN (SELECT * FROM @i WHERE StorageTable = 'Table1') AS I 
    ON T1.ParentId = I.ParentId 
<joins> 
<where clause> 

UNION ALL 

SELECT Col1, Col2, Col3 
FROM dbo.Table2 AS T2 
INNER JOIN (SELECT * FROM @i WHERE StorageTable = 'Table2') AS I 
    ON T2.ParentId = I.ParentId 
<joins> 
<where clause> 

UNION ALL 

SELECT Col1, Col2, Col3 
FROM dbo.Table3 AS T3 
INNER JOIN (SELECT * FROM @i WHERE StorageTable = 'Table3') AS I 
    ON T3.ParentId = I.ParentId 
<joins> 
<where clause> 
+0

您的連接也可以寫成:'INNER JOIN @i AS I ON T1.ParentId = I.ParentId AND I.StorageTable ='Table1',因此子查詢不是必需的。 你可以添加執行計劃嗎? – NickyvV

+0

執行計劃中顯示的成本在這種情況下不可靠。即使在實際計劃中,它們也只是估計的成本,因此不一定反映實際的運行時成本。 –

+0

請只發布(實際)計劃。它可以有任何數量的形狀。 – usr

回答

1

在這種情況下,您應該幾乎忽略子樹成本。

即使在實際的計劃中,他們只是基於估計。

從你所說的STATISTICS IO輸出中,例如訪問Table2的操作員的實際執行次數是0

但是該計劃可能會估計執行數= 1

(你可以看到在SSMS屬性窗口估計和實際數字選擇操作後)

如果該計劃的一些分支機構有您可以嘗試使用#temp表嘗試執行的數量的估計值,以便將列統計信息考慮在內。

通過添加一些幫助變量和OPTION (RECOMPILE),您可以獲得更具代表性的子樹成本,但它們仍然只與建模假設和估計值一樣精確。

例如

DECLARE @T TABLE(
    X   INT, 
    StorageTable VARCHAR(50)); 

INSERT INTO @T 
VALUES  (1, 'Table1') 

DECLARE @Branch1Exists BIT = iif(EXISTS(SELECT * FROM @T WHERE StorageTable = 'Table1'), 1, 0) 
DECLARE @Branch2Exists BIT = iif(EXISTS(SELECT * FROM @T WHERE StorageTable = 'Table2'), 1, 0) 

SELECT X 
FROM @T 
     JOIN master..spt_values V 
     ON [@T].X = number 
WHERE @Branch1Exists = 1 
UNION ALL 
SELECT X 
FROM @T 
     JOIN sys.objects 
     ON [@T].X = object_id 
WHERE @Branch2Exists = 1 
OPTION (recompile) 

移除在編譯時不執行,而不是示出了用於估計單次執行成本計劃的分支。

+0

感謝您的回覆。在這種情況下,或許STATISTICS IO是實際活動的一個更好的指標?就執行計劃的詳細信息而言,表2顯示估計操作員成本爲55%,預計執行次數= 5000(我應該提到每個表X有頂部(5000)),實際執行= 0,估計行數= 1,實際行數= 0.這似乎高估了執行次數,對吧?如果不適用,我只是不希望在Table2上花費資源。 –

+0

啊,我沒有注意到你有其他表參與''。如果只是表格變量會被估計爲執行1,除非你使用了'OPTION(RECOMPILE)'。但是,如果「Actual Executions = 0」,那麼成本基本上也是「0」。 –

+0

這很好,讓我的執行計劃更容易管理。感謝分享! –