為什麼子查詢將行估計減少到 1?
考慮以下人為但簡單的查詢:
SELECT ID , CASE WHEN ID <> 0 THEN (SELECT TOP 1 ID FROM X_OTHER_TABLE) ELSE (SELECT TOP 1 ID FROM X_OTHER_TABLE_2) END AS ID2 FROM X_HEAP;
我希望此查詢的最終行估計等於
X_HEAP
表中的行數。無論我在子查詢中做什麼,對行估計都無關緊要,因為它無法過濾掉任何行。但是,在 SQL Server 2016 上,由於子查詢,我看到行估計減少到 1:為什麼會這樣?我能做些什麼呢?
使用正確的語法很容易重現此問題。這是一組可以執行此操作的表定義:
CREATE TABLE dbo.X_HEAP (ID INT NOT NULL) CREATE TABLE dbo.X_OTHER_TABLE (ID INT NOT NULL); CREATE TABLE dbo.X_OTHER_TABLE_2 (ID INT NOT NULL); INSERT INTO dbo.X_HEAP WITH (TABLOCK) SELECT TOP (1000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM master..spt_values; CREATE STATISTICS X_HEAP__ID ON X_HEAP (ID) WITH FULLSCAN;
db小提琴連結。
在以下情況下,此基數估計 (CE) 問題會出現:
- 連接是帶有傳遞謂詞的外連接
- 直通謂詞的選擇性估計****正好是 1。
注意:用於確定選擇性的特定計算器並不重要。
細節
CE 將外連接的選擇性計算為以下各項之和:
- 具有相同謂詞的內連接選擇性
- 具有相同謂詞的反連接選擇性
外連接和內連接之間的唯一區別是外連接還返回與連接謂詞不匹配的行。反連接恰好提供了這種差異。內連接和反連接的基數估計比直接外連接更容易。
連接選擇性估計過程非常簡單:
- 首先,評估傳遞謂詞
的選擇性。
SPT
- 這是使用適合具體情況的任何計算器來完成的。
- 謂詞是整個事物,包括任何否定
IsFalseOrNull
成分。
- 內部連接選擇性:=
1 - SPT
- 反連接選擇性:=
SPT
反連接表示將“通過”連接的行。內連接表示不會“通過”的行。請注意,“通過”表示流過連接的行,根本不執行內側。強調一下:連接將返回所有行,區別在於在出現之前執行連接內側的行和不執行的行。
顯然,添加到應始終提供 1 的總選擇性,這意味著所有行都由連接返回,正如預期的那樣。
1 - SPT
****SPT
實際上,上述計算的工作原理與對除 1 之外的所有值的描述完全相同。
SPT
當= 1 時,內連接和反連接選擇性都估計為零,從而產生一行的基數估計(對於整個連接)。據我所知,這是無意的,應該報告為錯誤。
SPT
一個相關的問題
由於單獨的 CE 限制,此錯誤比人們想像的更可能出現。當
CASE
表達式使用EXISTS
子句時會出現這種情況(這很常見)。例如,問題中的以下修改查詢不會遇到意外的基數估計:-- This is fine SELECT CASE WHEN XH.ID = 1 THEN (SELECT TOP (1) XOT.ID FROM dbo.X_OTHER_TABLE AS XOT) END FROM dbo.X_HEAP AS XH;
引入一個小
EXISTS
問題確實會導致問題浮出水面:-- This is not fine SELECT CASE WHEN EXISTS (SELECT 1 WHERE XH.ID = 1) THEN (SELECT TOP (1) XOT.ID FROM dbo.X_OTHER_TABLE AS XOT) END FROM dbo.X_HEAP AS XH;
Using
EXISTS
在執行計劃中引入了一個半連接(突出顯示):半連接的估計很好。問題是 CE 將關聯的探針列視為一個簡單的投影,固定選擇性為 1:
Semijoin with probe column treated as a Project. Selectivity of probe column = 1
無論條款的內容如何,這都會自動滿足此 CE 問題出現所需的條件之一
EXISTS
。