2010-03-05 56 views
6

我有一個表項目值全中,看起來像2000兼容模式下運行的SQL 2005服務器上的數據(這是一個用戶定義的值表):SQL優化 - 執行計劃基於約束值更改 - 爲什麼?

ID ItemCode  FieldID Value 
-- ---------- ------- ------ 
1 abc123    1 D 
2 abc123    2 287.23 
4 xyz789    1 A 
5 xyz789    2 3782.23 
6 xyz789    3 23 
7 mno456    1 W 
9 mno456    3 45 
           ... and so on. 

FieldID來自在ItemField表:

ID FieldNumber DataFormatID Description ... 
-- ----------- ------------ ----------- 
1    1    1 Weight class 
2    2    4 Cost 
3    3    3 Another made up description 
.    .    x xxx 
.    .    x xxx 
.    .    x xxx 
x    91 (we have 91 user-defined fields) 

因爲我不能PIVOT在2000模式下,我們堅持用建設情況和GROUP BY來獲取數據的醜陋查詢一下應該如何對於s青梅遺留應用程序,這就是:

ItemNumber Field1 Field2 Field3 .... Field51 
---------- ------ ------- ------ 
    abc123 D  287.23 NULL 
    xyz789 A  3782.23 23 
    mno456 W  NULL  45 

你可以看到,我們只需要該表顯示值高達第51 UDF。這裏的查詢:

SELECT 
    iv.ItemNumber, 
    ,MAX(CASE WHEN f.FieldNumber = 1 THEN iv.[Value] ELSE NULL END) [Field1] 
    ,MAX(CASE WHEN f.FieldNumber = 2 THEN iv.[Value] ELSE NULL END) [Field2] 
    ,MAX(CASE WHEN f.FieldNumber = 3 THEN iv.[Value] ELSE NULL END) [Field3] 
     ... 
    ,MAX(CASE WHEN f.FieldNumber = 51 THEN iv.[Value] ELSE NULL END) [Field51] 
FROM ItemField f 
LEFT JOIN ItemValue iv ON f.ID = iv.FieldID 
WHERE f.FieldNumber <= 51 
GROUP BY iv.ItemNumber 

FieldNumber約束是< = 51,執行計劃進行類似:

SELECT <== Computer Scalar <== Stream Aggregate <== Sort (Cost: 70%) <== Hash Match <== (Clustered Index Seek && Table Scan)

而且速度快!我可以在大約一秒鐘內恢復100,000多條記錄,這符合我們的需求。

然而,如果我們有更多的UDF和我改變約束到任何上述(是的,我測試了他們一個接一個),或者如果我完全刪除了,我失去了排序的執行計劃,並被一大堆並行性塊取代,這些塊收集,重新分配和分配流,整個事情很慢(即使只有一條記錄,也只需30秒)。

FieldNumber具有羣集的,唯一的索引,並且是在ItemField表與ID柱(非聚集索引)複合主鍵的一部分。該項目值表的IDItemNumber列進行PK,並沒有對ItemNumber列額外的非聚集索引。

這是什麼原因?爲什麼更改我的簡單整數約束會改變整個執行計劃?

如果你決定了...... 你會做什麼不同?從現在開始計劃幾個月的SQL升級,但在此之前我需要解決此問題。

+0

我會做不同的是不使用表結構。使用具有定義字段的結構可以提前計算99%的數據需求,而不是使用EAV表。順便提一下,兼容模式並不妨礙您使用新功能,而是允許使用不再允許的功能。但是,如果您正在使用2000生產數據庫的2005數據庫進行開發,則最好避免使用新功能。 – HLGEM 2011-06-08 17:51:29

回答

4

SQL Server在優化查詢時足夠智能以考慮CHECK約束。

您的f.FieldNumber <= 51已經過優化,優化程序發現應該連接整個兩個表(最好用HASH JOIN完成)。

如果您沒有約束條件,引擎需要檢查條件,並且最可能使用索引遍歷來執行此操作。這可能會更慢。

可以請發佈查詢的整個計劃?只需運行SET SHOWPLAN_TEXT ON然後查詢。

更新:

這背後的理由?爲什麼更改我的簡單整數約束會改變整個執行計劃?

如果約束你的意思是WHERE條件,這可能是另一件事。

設置操作(這是SQL呢)沒有一個最有效的算法:每個算法的效率在很大程度上取決於在組數據分佈。

假設爲獲得一個子集(這就是WHERE子句所做的),您可以在索引中找到記錄的範圍,並使用索引記錄指針在表中找到數據行,或者只掃描所有記錄該表格並使用WHERE條件對它們進行過濾。

前操作的效率是m × const,後者是n,其中m是記錄的滿足條件的數量,n是在表中const > 1記錄的總數。

這意味着對於較大的值m,全掃描效率更高。

SQL Server意識到這一點,並相應地將執行計劃更改爲影響集合操作中數據分佈的常量。

爲此,SQL Server維護統計信息:每個索引列中數據分佈的彙總直方圖並使用它們構建查詢計劃。

因此,更改WHERE條件中的整數實際上會影響底層集的大小和數據分佈,並會使SQL Server重新考慮最適合處理該大小和佈局集的算法。

+0

我沒有在OP中看到CHECK約束的任何提及... – 2010-03-05 19:13:16

+0

我將發佈計劃,但我必須進行一些更改,以便我的真實表不會提供任何標識信息(我的示例已被修改)。 – 2010-03-05 20:41:44

+0

@Remus:當我讀到它時,我相信*當FieldNumber約束是<= 51 * *是關於一個'CHECK'約束,但你可能是對的,@op意味着'WHERE'條件。 – Quassnoi 2010-03-05 21:09:50

0

它被替換一大堆並行塊

試試這個:

SELECT 
    iv.ItemNumber, 
    ,MAX(CASE WHEN f.FieldNumber = 1 THEN iv.[Value] ELSE NULL END) [Field1] 
    ,MAX(CASE WHEN f.FieldNumber = 2 THEN iv.[Value] ELSE NULL END) [Field2] 
    ,MAX(CASE WHEN f.FieldNumber = 3 THEN iv.[Value] ELSE NULL END) [Field3] 
     ... 
    ,MAX(CASE WHEN f.FieldNumber = 51 THEN iv.[Value] ELSE NULL END) [Field51] 
FROM ItemField f 
LEFT JOIN ItemValue iv ON f.ID = iv.FieldID 
WHERE f.FieldNumber <= 51 
GROUP BY iv.ItemNumber 
OPTION (Maxdop 1) 

通過使用選項(MAXDOP 1),這應防止在執行計劃中的並行性。

+0

所以這實際上有效,但你不能把OPTION放在視圖中,因爲我需要它。 – 2010-03-05 18:55:54

0

在66你正在達到一些內部成本估算的門檻,決定一個計劃比較好。這個門檻是什麼以及它發生的原因並不重要。請注意,您的查詢與每個FieldNumber值不同,因爲您不僅要更改WHERE:還要更改僞'轉移'投影字段。

現在我不知道你的表的所有細節和你的查詢和插入/更新/刪除/模式,但對於特定的查詢您發佈的項目值表的正確聚集索引結構是這樣的:

CREATE CLUSTERED INDEX [cdxItemValue] ON ItemValue (FieldID, ItemNumber); 

該結構消除了對這個'數據透視'查詢結果進行中間排序的需要。