LIKE演算子のカーディナリティの推定(ローカル変数)


24

私はLIKE、未知のシナリオのすべての最適化で演算子を使用する場合、レガシーと新しいCEの両方が9%の見積もりを使用するという印象を受けました(関連する統計が利用可能であり、クエリオプティマイザーが選択性の推測に頼る必要がないと仮定)。

クレジットデータベースに対して以下のクエリを実行すると、CEごとに異なる推定値が得られます。新しいCEでは、予想していた900行の見積もりを受け取りますが、レガシーCEでは、241.416の見積もりを受け取りますが、この見積もりがどのように導出されるのかわかりません。誰もが光を当てることができますか?

-- New CE (Estimate = 900)
DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM [Credit].[dbo].[member]
WHERE [lastname] LIKE @LastName;

-- Forcing Legacy CE (Estimate = 241.416)
DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM [Credit].[dbo].[member]
WHERE [lastname] LIKE @LastName
OPTION (
QUERYTRACEON 9481,
QUERYTRACEON 9292,
QUERYTRACEON 9204,
QUERYTRACEON 3604
);

私のシナリオでは、既に互換性レベル120に設定されたクレジットデータベースがあります。そのため、2番目のクエリでトレースフラグを使用してレガシーCEを強制し、クエリオプティマイザーによって使用/考慮される統計に関する情報も提供します。「lastname」の列統計が使用されているのを見ることができますが、241.416の推定値がどのように導出されているのかまだわかりません。

オンライン以外には何も見つかりませんでした Itzik Ben-Ganの記事(「未知のシナリオのすべての最適化でLIKE述語を使用する場合、レガシーCEと新しいCEの両方が9%の見積もりを使用する」)。その投稿の情報は間違っているようです。

回答:


28

LIKE あなたの場合の推測は以下に基づいています:

  • G:標準の9%の推測(sqllang!x_Selectivity_Like
  • M:係数6(マジックナンバー)
  • D:(統計から)バイト単位の平均データ長、整数に切り捨て

具体的にsqllang!CCardUtilSQL7::ProbLikeGuessは、次を使用します。

Selectivity (S) = G / M * LOG(D)

ノート:

  • 次のLOG(D)場合、用語は省略されます。D1〜2のます。
  • 場合はD1未満である(欠落しているかを含めNULL、統計):
    D = FLOOR(0.5 * maximum column byte length)

この種の癖と複雑さは、元のCEの非常に典型的なものです。

質問の例では、平均長は5(5.6154から DBCC SHOW_STATISTICS)です。

見積もり= 10,000 *(0.09 / 6 * LOG(5))= 241.416

その他の値の例:

 D   = Sの式を使用した推定
 15 = 406.208
 14 = 395.859
 13 = 384.742
 12 = 372.736
 11 = 359.684
 10 = 345.388
 09 = 329.584
 08 = 311.916
 07 = 291.887
 06 = 268.764
 05 = 241.416
 04 = 207.944
 03 = 164.792
 02 = 150.000(ログは使用されません)
 01 = 150.000(ログは使用されません)
 00 = 291.887(ログ7)/ * FLOOR(0.5 * 15)[lastnameがvarchar(15)であるため15] * /

試験装置

DECLARE
    @CharLength integer = 5, -- Set length here
    @Counter integer = 1;

CREATE TABLE #T (c1 varchar(15) NULL);

-- Add 10,000 rows
SET NOCOUNT ON;
SET STATISTICS XML OFF;

BEGIN TRANSACTION;
WHILE @Counter <= 10000
BEGIN
    INSERT #T (c1) VALUES (REPLICATE('X', @CharLength));
    SET @Counter = @Counter + 1;
END;
COMMIT TRANSACTION;

SET NOCOUNT OFF;
SET STATISTICS XML ON;

-- Test query
DECLARE @Like varchar(15);
SELECT * FROM #T AS T 
WHERE T.c1 LIKE @Like;

DROP TABLE #T;

15

従来のCEを使用してSQL Server 2014でテストしたところ、カーディナリティの推定値として9%も得られませんでした。オンラインで正確なものを見つけることができなかったので、いくつかのテストを行い、試したすべてのテストケースに適合するモデルを見つけましたが、完全であることを確認することはできません。

私が見つけたモデルでは、推定値はテーブル内の行数、フィルター処理された列の統計の平均キー長、および場合によってはフィルター処理された列のデータ型の長さから導出されます。推定に使用される2つの異なる式があります。

FLOOR(平均キー長)= 0の場合、推定式は列統計を無視し、データ型の長さに基づいて推定を作成します。VARCHAR(N)でのみテストしたため、NVARCHAR(N)には異なる式がある可能性があります。VARCHAR(N)の式は次のとおりです。

(行の推定値)=(表の行)*(-0.004869 + 0.032649 * log10(データ型の長さ))

これは非常にうまくフィットしますが、完全に正確ではありません。

最初の数式グラフ

x軸はデータ型の長さ、y軸は100万行のテーブルの推定行数です。

クエリオプティマイザーは、列の統計情報がない場合、または列の平均キー長を1未満にするのに十分なNULL値がある場合、この式を使用します。

たとえば、VARCHAR(50)でフィルタリングを行い、列統計を持たない150k行のテーブルがあると仮定します。行推定の予測は次のとおりです。

150000 *(-0.004869 + 0.032649 * log10(50))= 7590.1行

テストするSQL:

CREATE TABLE X_CE_LIKE_TEST_1 (
STRING VARCHAR(50)
);

CREATE STATISTICS X_STAT_CE_LIKE_TEST_1 ON X_CE_LIKE_TEST_1 (STRING) WITH NORECOMPUTE;

WITH
    L0 AS (SELECT 1 AS c UNION ALL SELECT 1),
    L1 AS (SELECT 1 AS c FROM L0 A CROSS JOIN L0 B),
    L2 AS (SELECT 1 AS c FROM L1 A CROSS JOIN L1 B),
    L3 AS (SELECT 1 AS c FROM L2 A CROSS JOIN L2 B),
    L4 AS (SELECT 1 AS c FROM L3 A CROSS JOIN L3 B CROSS JOIN L2 C),
    NUMS AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS NUM FROM L4)  
    INSERT INTO X_CE_LIKE_TEST_1 WITH (TABLOCK) (STRING)
    SELECT TOP (150000) 'ZZZZZ'
    FROM NUMS
    ORDER BY NUM;

DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM X_CE_LIKE_TEST_1
WHERE STRING LIKE @LastName;

SQL Serverは7242.47の推定行カウントを提供しますが、これは近い値です。

FLOOR(平均キー長)> = 1の場合、FLOOR(平均キー長)の値に基づく異なる式が使用されます。ここに私が試したいくつかの値の表があります:

1    1.5%
2    1.5%
3    1.64792%
4    2.07944%
5    2.41416%
6    2.68744%
7    2.91887%
8    3.11916%
9    3.29584%
10   3.45388%

FLOOR(平均キー長)<6の場合、上記の表を使用します。それ以外の場合は、次の式を使用します。

(行の見積もり)=(表の行)*(-0.003381 + 0.034539 * log10(FLOOR(平均キー長)))

これは他のものよりもフィット感が優れていますが、それでも完全に正確ではありません。

二次式グラフ

x軸は平均キー長で、y軸は100万行のテーブルの推定行数です。

別の例を挙げると、フィルター処理された列の統計用に平均キー長が5.5の1万行のテーブルがあると仮定します。行の見積もりは次のようになります。

10000 * 0.241416 = 241.416行。

テストするSQL:

CREATE TABLE X_CE_LIKE_TEST_2 (
STRING VARCHAR(50)
);

WITH
    L0 AS (SELECT 1 AS c UNION ALL SELECT 1),
    L1 AS (SELECT 1 AS c FROM L0 A CROSS JOIN L0 B),
    L2 AS (SELECT 1 AS c FROM L1 A CROSS JOIN L1 B),
    L3 AS (SELECT 1 AS c FROM L2 A CROSS JOIN L2 B),
    L4 AS (SELECT 1 AS c FROM L3 A CROSS JOIN L3 B CROSS JOIN L2 C),
    NUMS AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS NUM FROM L4)  
    INSERT INTO X_CE_LIKE_TEST_2 WITH (TABLOCK) (STRING)
    SELECT TOP (10000) 
    CASE 
      WHEN NUM % 2 = 1 THEN REPLICATE('Z', 5) 
      ELSE REPLICATE('Z', 6)
    END
    FROM NUMS
    ORDER BY NUM;

CREATE STATISTICS X_STAT_CE_LIKE_TEST_2 ON X_CE_LIKE_TEST_2 (STRING) 
WITH NORECOMPUTE, FULLSCAN;

DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM X_CE_LIKE_TEST_2
WHERE STRING LIKE @LastName;

行の見積もりは241.416で、質問の内容と一致します。テーブルにない値を使用すると、エラーが発生します。

ここのモデルは完全ではありませんが、一般的な動作をかなりよく示していると思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.