謂詞的直接值產生不太好的計劃
我正在使用 StackOverflow 轉儲來執行一些測試。
特別是,我正在查詢這張表:
我創建了這個索引:
我正在執行以下查詢(只是強制索引來測試替代方案)
我得到以下執行計劃的成本很高(66.63)。
這些是執行此查詢後的 IO 統計資訊:
然後,我通過提供變數而不是直接值來執行相同的查詢
我得到了一個更好的計劃(成本是 0.4385)。
統計數據也更好:
起初…我認為 SQL Server 沒有將直接值辨識為 INT,但既沒有類型不匹配也沒有隱式轉換警告。
我還嘗試避免並行性,但在謂詞中傳遞直接值時,我仍然會使用 MAXDOP 1 獲得高成本計劃(和更高的 IO 統計資訊)。
在比較兩個計劃時,有不同的估計:
作為謂詞的一部分傳遞直接值有什麼問題?
作為謂詞的一部分傳遞直接值有什麼問題?
變數和參數的區別
優化器在計算變數的估計值時使用統計密度向量。
當“直接”或“靜態”值直接嵌入到查詢中時,將使用統計直方圖。這就是為什麼你會得到不同的估計,從而得到不同的計劃。
這是我的估計計劃:https ://www.brentozar.com/pastetheplan/?id=SJCduTuKN
在我 2010 年的 SO 數據庫副本中,OwnerUserId 列的密度為 0.000003807058。乘以 3,744,192 行 = 14.2544 行。這正是 IX_Posts_OwnerUserId 估計出來的行數。
您可以通過執行以下 DBCC 命令獲取有關該索引的統計資訊的資訊:
DBCC SHOW_STATISTICS('dbo.Posts', 'IX_Posts_OwnerUserId');
這是(縮寫)輸出:
Name Updated Rows IX_Posts_OwnerUserId Apr 8 2019 8:33AM 3744192 All density Average Length Columns 3.807058E-06 4 OwnerUserId
由於 PostTypeId 也是 WHERE 子句的一部分,因此也會自動為該列生成統計資訊。該密度向量為 0.25 x 3,744,192 行 = 936,048 行。
DBCC SHOW_STATISTICS('dbo.Posts', '_WA_Sys_00000010_0519C6AF');
和輸出:
Name Updated Rows _WA_Sys_00000010_0519C6AF Apr 8 2019 9:04AM 3744192 All density Average Length Columns 0.25 4 PostTypeId
由於這是一個“AND”謂詞,因此估計使用兩者中的較低值。
當您使用靜態值而不是變數時,它使用統計直方圖。這是該 SHOW_STATISTICS 命令的第三個結果集中。對於您正在使用的鍵,這是直方圖條目:
RANGE_HI_KEY RANGE_ROWS EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS 22656 13040 11371 305 42.7541
這就是“靜態值”計劃中 11,371 的估計值的來源。
直方圖在很多時候可以是一個更好的估計,因為它可以更好地處理邊緣情況——因為像這樣的大表中經常會有一些異常值。
成本核算差異
在這種特定情況下,直方圖會產生一個完全正確的估計。生成的計劃的成本(正確地)高於使用密度向量的成本,因為它必須處理更多的行。
“低成本”計劃認為該搜尋將產生 14 行,而實際上產生了 11,371 行。
邏輯讀取
由於嵌套循環預取,並行計劃中的邏輯讀取略高。在我的機器上似乎並沒有太大的區別——查詢的經過時間在 10 毫秒之內。
並行性實際上沒有任何幫助,因為所有行最終都在一個執行緒上(無論如何在我的機器上)。添加
OPTION (MAXDOP 1)
有助於縮短執行時間,但不會刪除額外的邏輯讀取。此查詢的“額外讀取”問題的一個潛在解決方案是通過將 PostTypeId 添加為包含列來完全避免鍵查找:
CREATE INDEX IX_Posts_OwnerUserId ON dbo.Posts (OwnerUserId) INCLUDE (PostTypeId) WITH (DROP_EXISTING = ON);