文字列列と特定の長さの行をチェックする述語を持つテーブルがあります。 SQL Server 2014では、チェックしている長さに関係なく、1行の見積もりが表示されます。実際には数千または数百万の行があり、SQL Serverはこのテーブルをネストされたループの外側に配置することを選択しているため、これは非常に貧弱な計画を生み出しています。
SQL Server 2014のカーディナリティの推定値1.0003に対する説明はありますか?SQL Server 2012は31,622行を推定しますか?良い回避策はありますか?
これは問題の短い複製です:
-- Create a table with 1MM rows of dummy data
CREATE TABLE #customers (cust_nbr VARCHAR(10) NOT NULL)
GO
INSERT INTO #customers WITH (TABLOCK) (cust_nbr)
SELECT TOP 1000000
CONVERT(VARCHAR(10),
ROW_NUMBER() OVER (ORDER BY (SELECT NULL))) AS cust_nbr
FROM master..spt_values v1
CROSS JOIN master..spt_values v2
GO
-- Looking for string of a certain length.
-- While both CEs yield fairly poor estimates, the 2012 CE is much
-- more conservative (higher estimate) and therefore much more likely
-- to yield an okay plan rather than a drastically understimated loop join.
-- 2012: 31,622 rows estimated, 900K rows actual
-- 2014: 1 row estimated, 900K rows actual
SELECT COUNT(*)
FROM #customers
WHERE LEN(cust_nbr) = 6
OPTION (QUERYTRACEON 9481) -- Optionally, use 2012 CE
GO
SQL Server 2014 Cardinality Estimatorに関するホワイトペーパー も読みましたが、状況を明らかにするものは見つかりませんでした。
レガシーCEの場合、行の3.16228%の見積もりであることがわかります。これは、column =リテラル述語に使用される「マジックナンバー」ヒューリスティックです(述語の構築に基づく他のヒューリスティックもありますが、LEN
レガシーCEの結果の列を囲むと、この推測フレームワークと一致します)。この例は、Joe Sackによる 統計がない場合の選択性推測 、およびIan Joseによる 定数-定数比較推定 の投稿で確認できます。
-- Legacy CE: 31622.8 rows
SELECT COUNT(*)
FROM #customers
WHERE LEN(cust_nbr) = 6
OPTION ( QUERYTRACEON 9481); -- Legacy CE
GO
新しいCEの動作については、これはオプティマイザから見えるようになりました(つまり、統計を使用できます)。以下の計算機の出力を見ることで、関連する統計の自動生成をポインターとして見ることができます。
-- New CE: 1.00007 rows
SELECT COUNT(*)
FROM #customers
WHERE LEN(cust_nbr) = 6
OPTION ( QUERYTRACEON 2312 ); -- New CE
GO
-- View New CE behavior with 2363 (for supported option use XEvents)
SELECT COUNT(*)
FROM #customers
WHERE LEN(cust_nbr) = 6
OPTION (QUERYTRACEON 2312, QUERYTRACEON 2363, QUERYTRACEON 3604, RECOMPILE); -- New CE
GO
/*
Loaded histogram for column QCOL:
[tempdb].[dbo].[#customers].cust_nbr from stats with id 2
Using ambient cardinality 1e+006 to combine distinct counts:
999927
Combined distinct count: 999927
Selectivity: 1.00007e-006
Stats collection generated:
CStCollFilter(ID=2, CARD=1.00007)
CStCollBaseTable(ID=1, CARD=1e+006 TBL: #customers)
End selectivity computation
*/
EXEC tempdb..sp_helpstats '#customers';
--Check out AVG_RANGE_ROWS values (for example - plenty of ~ 1)
DBCC SHOW_STATISTICS('tempdb..#customers', '_WA_Sys_00000001_B0368087');
--That's my Stats name yours is subject to change
残念ながら、このロジックはLEN
関数の効果に対して調整されていない個別の値の数の見積もりに依存しています。
LEN
をLIKE
として書き換えることで、両方のCEモデルでトライベースの推定を取得できます。
SELECT COUNT_BIG(*)
FROM #customers AS C
WHERE C.cust_nbr LIKE REPLICATE('_', 6);
使用されるトレースフラグに関する情報:
SQL 2012の基数推定値1.0003の説明はありますが、SQL 2012は31,622行を推定しますか?
@Zaneの answer はこの部分をかなりカバーしていると思います。
良い回避策はありますか?
LEN(cust_nbr)
の非永続計算列を作成して、(オプションで)その計算列に非クラスター化インデックスを作成してみてください。これで正確な統計が得られます。
私はいくつかのテストを行い、ここに私が見つけたものがあります:
PERSISTED
(インデックスなし)にすることは、他の2つのバリエーションよりも優れていました。推定行はより正確でした。 CPUと経過時間はより優れていました(行ごとに何も計算する必要がないため、予想どおり)。PERSISTED
であっても、(計算されているため)計算された列にフィルターインデックスまたはフィルター統計を作成できませんでした:-(