SQL Server 2014でLEN()関数がカーディナリティを過小評価するのはなぜですか?


26

文字列列と特定の長さの行をチェックする述語を持つテーブルがあります。SQL Server 2014では、チェックする長さに関係なく、1行の推定値が表示されます。実際には数千または数百万の行があり、SQL Serverはこのテーブルをネストされたループの外側に配置することを選択しているため、これは非常に貧弱な計画を生み出しています。

SQL Server 2012で31,622行を見積もる一方で、SQL Server 2014の1.0003のカーディナリティの見積もりについての説明はありますか?良い回避策はありますか?

問題の簡単な複製を次に示します。

-- 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ホワイトペーパーも読んでいますが、状況を明確にするものは何も見つかりませんでした。

回答:


20

レガシーCEの場合、推定値は行の3.16228%であり、これはcolumn = literal述語に使用される「マジックナンバー」ヒューリスティックです(述語構築に基づいた他のヒューリスティックがLENありますが、レガシーCEの結果は、この推測フレームワークに一致します)。この例は、Joe Sackによる統計の不在下でのSelectivity Guessesに関する投稿、および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をaとして書き換えることにより、両方のCEモデルでトライベースの推定値を取得できますLIKE

SELECT COUNT_BIG(*)
FROM #customers AS C
WHERE C.cust_nbr LIKE REPLICATE('_', 6);

いいねプラン


使用されるトレースフラグに関する情報:

  • 2363:読み込まれている統計を含む多くの情報を表示します。
  • 3604:DBCCコマンドの出力をメッセージタブに出力します。

13

SQL 2014のカーディナリティ推定値1.0003にSQL 2012の31,622行の推定値の説明はありますか?

@Zaneの答えは、この部分をかなりうまくカバーしていると思います。

良い回避策はありますか?

計算列に対して非永続計算列をLEN(cust_nbr)作成し、(オプションで)その計算列に非クラスター化インデックスを作成してみてください。これにより、正確な統計情報が得られます。

私はいくつかのテストを行いましたが、ここに私が見つけたものがあります:

  • 統計は、インデックスが定義されていない場合、非永続計算列で自動作成されました。
  • 計算列に非クラスター化インデックスを追加しても効果がなかっただけでなく、実際にはパフォーマンスが少し低下します。わずかに高いCPUおよび経過時間。わずかに高い推定コスト(価値があるものは何でも)。
  • 計算列をPERSISTED(インデックスなし)にすることは、他の2つのバリエーションよりも優れていました。推定行はより正確でした。CPUと経過時間は優れていました(行ごとに何も計算する必要がないため、予想どおり)。
  • PERSISTED:-( 計算されたために)計算された列にフィルターされたインデックスまたはフィルターされた統計を作成できませんでした:-(

1
永続的なものとそうでないものを徹底的に比較してくれてありがとう。永続化された計算列に利点がある場合でも、式の統計が有益な場合には、非永続化はオーバーヘッドが非常に少ない非常に迅速な勝利になる可能性があることを知っておくと便利です。
ジェフパターソン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.