web-dev-qa-db-ja.com

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

不明なシナリオのすべての最適化でLIKE演算子を使用する場合、レガシーCEと新しい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%の見積もりを使用する」と述べています。 。その投稿の情報は正しくないようです。

24
Fza

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

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

具体的には、_sqllang!CCardUtilSQL7::ProbLikeGuess_は以下を使用します。

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

ノート:

  • Dが1〜2の場合、LOG(D)項は省略されます。
  • Dが1未満の場合(欠落またはNULL統計を含む):
    D = FLOOR(0.5 * maximum column byte length)

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

質問の例では、平均の長さは5です(_DBCC SHOW_STATISTICS_からの5.6154は切り捨てられます)。

見積もり= 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(LOG未使用)
 01 = 150.000(LOG未使用)
 00 = 291.887(LOG 7)/ * FLOOR(0.5 * 15) [ラストネームは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;
_
28
Paul White 9

レガシーCEを使用してSQL Server 2014でテストしましたが、基数の見積もりとして9%も得られませんでした。オンラインで正確なものを見つけることができなかったので、いくつかのテストを行ったところ、試行したすべてのテストケースに適合するモデルが見つかりましたが、それが完全であるかどうかはわかりません。

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

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

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

これは非常にうまく適合していますが、完全に正確ではありません。

first formula graph

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(平均キー長)))

これは他のものよりも適合性が優れていますが、まだ完全に正確ではありません。

second formula graph

X軸は平均キー長であり、Y軸は100万行のテーブルの推定行数です。

別の例として、フィルターされた列の統計用に、平均キー長が5.5の行が10k行あるテーブルがあるとします。行の見積もりは次のようになります。

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で、これは質問の内容と一致します。表にない値を使用すると、エラーが発生します。

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

15
Joe Obbish