統計でヒストグラムのステップ数はどのように決定されますか


11

SQL Serverの統計でヒストグラムのステップ数はどのように決定されますか?

キー列に200以上の異なる値があるにもかかわらず、なぜ200ステップに制限されているのですか?決め手はありますか?


デモ

スキーマ定義

CREATE TABLE histogram_step
  (
     id   INT IDENTITY(1, 1),
     name VARCHAR(50),
     CONSTRAINT pk_histogram_step PRIMARY KEY (id)
  )

テーブルに100レコードを挿入する

INSERT INTO histogram_step
            (name)
SELECT TOP 100 name
FROM   sys.syscolumns

統計の更新と確認

UPDATE STATISTICS histogram_step WITH fullscan

DBCC show_statistics('histogram_step', pk_histogram_step)

ヒストグラムの手順:

+--------------+------------+---------+---------------------+----------------+
| RANGE_HI_KEY | RANGE_ROWS | EQ_ROWS | DISTINCT_RANGE_ROWS | AVG_RANGE_ROWS |
+--------------+------------+---------+---------------------+----------------+
|            1 |          0 |       1 |                   0 |              1 |
|            3 |          1 |       1 |                   1 |              1 |
|            5 |          1 |       1 |                   1 |              1 |
|            7 |          1 |       1 |                   1 |              1 |
|            9 |          1 |       1 |                   1 |              1 |
|           11 |          1 |       1 |                   1 |              1 |
|           13 |          1 |       1 |                   1 |              1 |
|           15 |          1 |       1 |                   1 |              1 |
|           17 |          1 |       1 |                   1 |              1 |
|           19 |          1 |       1 |                   1 |              1 |
|           21 |          1 |       1 |                   1 |              1 |
|           23 |          1 |       1 |                   1 |              1 |
|           25 |          1 |       1 |                   1 |              1 |
|           27 |          1 |       1 |                   1 |              1 |
|           29 |          1 |       1 |                   1 |              1 |
|           31 |          1 |       1 |                   1 |              1 |
|           33 |          1 |       1 |                   1 |              1 |
|           35 |          1 |       1 |                   1 |              1 |
|           37 |          1 |       1 |                   1 |              1 |
|           39 |          1 |       1 |                   1 |              1 |
|           41 |          1 |       1 |                   1 |              1 |
|           43 |          1 |       1 |                   1 |              1 |
|           45 |          1 |       1 |                   1 |              1 |
|           47 |          1 |       1 |                   1 |              1 |
|           49 |          1 |       1 |                   1 |              1 |
|           51 |          1 |       1 |                   1 |              1 |
|           53 |          1 |       1 |                   1 |              1 |
|           55 |          1 |       1 |                   1 |              1 |
|           57 |          1 |       1 |                   1 |              1 |
|           59 |          1 |       1 |                   1 |              1 |
|           61 |          1 |       1 |                   1 |              1 |
|           63 |          1 |       1 |                   1 |              1 |
|           65 |          1 |       1 |                   1 |              1 |
|           67 |          1 |       1 |                   1 |              1 |
|           69 |          1 |       1 |                   1 |              1 |
|           71 |          1 |       1 |                   1 |              1 |
|           73 |          1 |       1 |                   1 |              1 |
|           75 |          1 |       1 |                   1 |              1 |
|           77 |          1 |       1 |                   1 |              1 |
|           79 |          1 |       1 |                   1 |              1 |
|           81 |          1 |       1 |                   1 |              1 |
|           83 |          1 |       1 |                   1 |              1 |
|           85 |          1 |       1 |                   1 |              1 |
|           87 |          1 |       1 |                   1 |              1 |
|           89 |          1 |       1 |                   1 |              1 |
|           91 |          1 |       1 |                   1 |              1 |
|           93 |          1 |       1 |                   1 |              1 |
|           95 |          1 |       1 |                   1 |              1 |
|           97 |          1 |       1 |                   1 |              1 |
|           99 |          1 |       1 |                   1 |              1 |
|          100 |          0 |       1 |                   0 |              1 |
+--------------+------------+---------+---------------------+----------------+

ご覧のように、ヒストグラムには53のステップがあります。

再び数千のレコードを挿入

INSERT INTO histogram_step
            (name)
SELECT TOP 10000 b.name
FROM   sys.syscolumns a
       CROSS JOIN sys.syscolumns b

統計の更新と確認

UPDATE STATISTICS histogram_step WITH fullscan

DBCC show_statistics('histogram_step', pk_histogram_step)

ヒストグラムのステップが4ステップに減少しました

+--------------+------------+---------+---------------------+----------------+
| RANGE_HI_KEY | RANGE_ROWS | EQ_ROWS | DISTINCT_RANGE_ROWS | AVG_RANGE_ROWS |
+--------------+------------+---------+---------------------+----------------+
|            1 |          0 |       1 |                   0 |              1 |
|        10088 |      10086 |       1 |               10086 |              1 |
|        10099 |         10 |       1 |                  10 |              1 |
|        10100 |          0 |       1 |                   0 |              1 |
+--------------+------------+---------+---------------------+----------------+

再び数千のレコードを挿入

INSERT INTO histogram_step
            (name)
SELECT TOP 100000 b.name
FROM   sys.syscolumns a
       CROSS JOIN sys.syscolumns b

統計の更新と確認

UPDATE STATISTICS histogram_step WITH fullscan

DBCC show_statistics('histogram_step', pk_histogram_step) 

ヒストグラムのステップが3ステップに減少しました

+--------------+------------+---------+---------------------+----------------+
| RANGE_HI_KEY | RANGE_ROWS | EQ_ROWS | DISTINCT_RANGE_ROWS | AVG_RANGE_ROWS |
+--------------+------------+---------+---------------------+----------------+
|            1 |          0 |       1 |                   0 |              1 |
|       110099 |     110097 |       1 |              110097 |              1 |
|       110100 |          0 |       1 |                   0 |              1 |
+--------------+------------+---------+---------------------+----------------+

誰かがこれらのステップがどのように決定されるのか教えてくれますか?


3
200は任意の選択でした。特定のテーブルにある個別の値の数とは関係ありません。あなたは200が選ばれた理由を知りたい場合は、1990年代SQL Serverチームからの技術者に依頼する必要がありますないあなたの仲間
アーロン・ベルトラン

1
@AaronBertrand-ありがとう..では、これらのステップ数はどのように決定されるのですか
Pரதீப்

1
決定はありません。上限は200です。期間。まあ、技術的には201ですが、それは別の日の話です。
アーロンバートランド

1
私は役に立つかもしれません、intrastep推定値について同様の質問をしてきたdba.stackexchange.com/questions/148523/...
jesijesi

回答:


14

このコラムでは、単一列の統計についてのみ説明します。これは、かなり長くなるため、SQL Serverがデータをヒストグラムステップにバケット化する方法に関心があるためです。複数列統計の場合、ヒストグラムは先行列でのみ作成されます。

SQL Serverは、統計の更新が必要であると判断すると、テーブルのすべてのデータまたはテーブルのデータのサンプルのいずれかを読み取る非表示のクエリを開始します。これらのクエリは、拡張イベントで表示できます。StatManヒストグラムの作成に関連するSQL Server内で呼び出される関数があります。単純な統計オブジェクトの場合、少なくとも2つの異なるタイプのStatManクエリがあります(迅速な統計更新のための異なるクエリがあり、パーティション化されたテーブルの増分統計機能も異なるクエリを使用していると思います)。

最初のものは、フィルタリングせずにテーブルからすべてのデータを取得します。これは、テーブルが非常に小さい場合、またはFULLSCANオプションを使用して統計を収集する場合に確認できます。

CREATE TABLE X_SHOW_ME_STATMAN (N INT);
CREATE STATISTICS X_STAT_X_SHOW_ME_STATMAN ON X_SHOW_ME_STATMAN (N);

-- after gathering stats with 1 row in table
SELECT StatMan([SC0]) FROM
(
    SELECT TOP 100 PERCENT [N] AS [SC0] 
    FROM [dbo].[X_SHOW_ME_STATMAN] WITH (READUNCOMMITTED)
    ORDER BY [SC0] 
) AS _MS_UPDSTATS_TBL 
OPTION (MAXDOP 16);

SQL Serverは、テーブルのサイズに基づいて自動サンプルサイズを選択します(テーブルの行数とページ数の両方だと思います)。テーブルが大きすぎる場合、自動サンプルサイズは100%未満になります。これは、100万行の同じテーブルで取得したものです。

-- after gathering stats with 1 M rows in table
SELECT StatMan([SC0], [SB0000]) FROM 
(
    SELECT TOP 100 PERCENT [SC0], step_direction([SC0]) over (order by NULL) AS [SB0000] 
    FROM 
    (
        SELECT [N] AS [SC0] 
        FROM [dbo].[X_SHOW_ME_STATMAN] TABLESAMPLE SYSTEM (6.666667e+001 PERCENT) WITH (READUNCOMMITTED) 
    ) AS _MS_UPDSTATS_TBL_HELPER 
    ORDER BY [SC0], [SB0000] 
) AS _MS_UPDSTATS_TBL
OPTION (MAXDOP 1);

TABLESAMPLEドキュメント化されてますが、StatManとstep_direction はドキュメント化されていません。ここでSQL Serverは、テーブルからのデータの約66.6%をサンプリングしてヒストグラムを作成します。これはFULLSCAN、同じデータの統計を(なしで)更新するときに、異なる数のヒストグラムステップを取得できることを意味します。私は実際にこれを観察したことがありませんが、なぜそれが可能ではないのかわかりません。

単純なデータに対していくつかのテストを実行して、統計が時間とともにどのように変化するかを見てみましょう。以下は、テーブルに連続した整数を挿入し、各挿入後に統計を収集し、統計に関する情報を結果テーブルに保存するために書いたテストコードです。一度に1行ずつ最大10000まで挿入するところから始めましょう。テストベッド:

DECLARE
@stats_id INT,
@table_object_id INT,
@rows_per_loop INT = 1,
@num_of_loops INT = 10000,
@loop_num INT;

BEGIN
    SET NOCOUNT ON;

    TRUNCATE TABLE X_STATS_RESULTS;

    SET @table_object_id = OBJECT_ID ('X_SEQ_NUM');
    SELECT @stats_id = stats_id FROM sys.stats
    WHERE OBJECT_ID = @table_object_id
    AND name = 'X_STATS_SEQ_INT_FULL';

    SET @loop_num = 0;
    WHILE @loop_num < @num_of_loops
    BEGIN
        SET @loop_num = @loop_num + 1;

        INSERT INTO X_SEQ_NUM WITH (TABLOCK)
        SELECT @rows_per_loop * (@loop_num - 1) + N FROM dbo.GetNums(@rows_per_loop);

        UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN; -- can comment out FULLSCAN as needed

        INSERT INTO X_STATS_RESULTS WITH (TABLOCK)
        SELECT 'X_STATS_SEQ_INT_FULL', @rows_per_loop * @loop_num, rows_sampled, steps 
        FROM sys.dm_db_stats_properties(@table_object_id, @stats_id);
        END;
END;

このデータの場合、ヒストグラムのステップ数はすぐに200に増加し(最初は397行で最大ステップ数に達します)、1485行がテーブルに表示されるまで199または200に留まり、ヒストグラムが3または4になるまでゆっくりと減少します。ステップ。すべてのデータのグラフは次のとおりです。

最初のグラフ

これは、1万行のヒストグラムです。

RANGE_HI_KEY    RANGE_ROWS  EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
1               0           1       0                   1
9999            9997        1       9997                1
10000           0           1       0                   1

ヒストグラムに3つのステップしかないのは問題ですか?情報は私たちの観点から保存されているようです。データ型がINTEGERであるため、1〜10000の整数ごとにテーブル内の行数を把握できます。通常、SQL Serverもこれを把握できますが、うまくいかない場合もあります。 。この例については、このSEの投稿を参照してください。

テーブルから単一の行を削除して統計を更新するとどうなりますか?理想的には、不足している整数がテーブルにないことを示す別のヒストグラムステップを取得します。

DELETE FROM X_SEQ_NUM
WHERE X_NUM  = 1000;

UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;

DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- still 3 steps

DELETE FROM X_SEQ_NUM
WHERE X_NUM  IN (2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000);

UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;

DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- still 3 steps

それは少しがっかりです。手作業でヒストグラムを作成する場合は、欠損値ごとにステップを追加します。SQL Serverは汎用アルゴリズムを使用しているため、一部のデータセットでは、使用するコードよりも適切なヒストグラムを作成できる場合があります。もちろん、テーブルから0行または1行を取得することの実際的な違いはごくわずかです。テーブル内の各整数が2行である20000行でテストすると、同じ結果が得られます。データを削除すると、ヒストグラムにステップが追加されません。

RANGE_HI_KEY    RANGE_ROWS  EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
1               0           2       0                   1
9999            19994       2       9997                2
10000           0           2       0                   1

テーブル内の各整数が100行である100万行でテストすると、少し良い結果が得られますが、手動でより良いヒストグラムを作成できます。

truncate table X_SEQ_NUM;

BEGIN TRANSACTION;
INSERT INTO X_SEQ_NUM WITH (TABLOCK)
SELECT N FROM dbo.GetNums(10000);
GO 100
COMMIT TRANSACTION;

UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;

DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- 4 steps

DELETE FROM X_SEQ_NUM
WHERE X_NUM  = 1000;

UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;

DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- now 5 steps with a RANGE_HI_KEY of 998 (?)

DELETE FROM X_SEQ_NUM
WHERE X_NUM  IN (2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000);

UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;

DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- still 5 steps

最終ヒストグラム:

RANGE_HI_KEY    RANGE_ROWS  EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
1               0           100     0                   1
998             99600       100     996                 100
3983            298100      100     2981                100
9999            600900      100     6009                100
10000           0           100     0                   1

連続した整数でさらにテストしてみましょう。ただし、テーブルの行数は多くなります。小さすぎるテーブルの場合、手動でサンプルサイズを指定しても効果がないため、挿入ごとに100行を追加し、毎回最大100万行まで統計を収集します。以前と同様のパターンが表示されますが、テーブルの行が637300行になると、デフォルトのサンプルレートでテーブルの行を100%サンプリングしなくなります。行を取得すると、ヒストグラムのステップ数が増加します。おそらくこれは、テーブル内の非サンプリング行の数が増えると、SQL Serverがデータのギャップを増やしてしまうためです。100万行でも200ステップはヒットしませんが、行を追加し続けると、そこに到達し、最終的に下に戻り始めます。

グラフ2

X軸はテーブルの行数です。行数が増えると、サンプリングされる行は少し異なり、650kを超えません。

次に、VARCHARデータを使用して簡単なテストを実行します。

CREATE TABLE X_SEQ_STR (X_STR VARCHAR(5));
CREATE STATISTICS X_SEQ_STR ON X_SEQ_STR(X_STR);

ここでは、NULLとともに200個の数値(文字列として)を挿入しています。

INSERT INTO X_SEQ_STR
SELECT N FROM dbo.GetNums(200)
UNION ALL
SELECT NULL;

UPDATE STATISTICS X_SEQ_STR X_SEQ_STR ;

DBCC SHOW_STATISTICS ('X_SEQ_STR', 'X_SEQ_STR'); -- 111 steps, RANGE_ROWS is 0 or 1 for all steps

NULLは、テーブルで検出されると、常に独自のヒストグラムステップを取得することに注意してください。SQL Serverは、すべての情報を保存するために正確に201の手順を私に与えたかもしれませんが、それはしませんでした。たとえば、「1111」は「1」と「2」の間でソートされるため、技術情報は失われます。

整数だけでなく、別の文字を挿入してみましょう。

truncate table X_SEQ_STR;

INSERT INTO X_SEQ_STR
SELECT CHAR(10 + N) FROM dbo.GetNums(200)
UNION ALL
SELECT NULL;

UPDATE STATISTICS X_SEQ_STR X_SEQ_STR ;

DBCC SHOW_STATISTICS ('X_SEQ_STR', 'X_SEQ_STR'); -- 95 steps, RANGE_ROWS is 0 or 1 or 2

前回のテストとの違いはありません。

次に、文字を挿入してみますが、テーブルに各文字の数を変えてみましょう。たとえば、CHAR(11)1行ある、CHAR(12)2行ある、などです。

truncate table X_SEQ_STR;

DECLARE
@loop_num INT;

BEGIN
    SET NOCOUNT ON;

    SET @loop_num = 0;
    WHILE @loop_num < 200
    BEGIN
        SET @loop_num = @loop_num + 1;

        INSERT INTO X_SEQ_STR WITH (TABLOCK)
        SELECT CHAR(10 + @loop_num) FROM dbo.GetNums(@loop_num);
    END;
END;

UPDATE STATISTICS X_SEQ_STR X_SEQ_STR ;

DBCC SHOW_STATISTICS ('X_SEQ_STR', 'X_SEQ_STR'); -- 148 steps, most with RANGE_ROWS of 0

前と同じように、まだ200ヒストグラムのステップを取得できません。ただし、ステップの多くはRANGE_ROWS0です。

最後のテストでは、各ループに5文字のランダムな文字列を挿入し、毎回統計を収集します。これがランダムな文字列のコードです:

char((rand()*25 + 65))+char((rand()*25 + 65))+char((rand()*25 + 65))+char((rand()*25 + 65))+char((rand()*25 + 65))

以下は、テーブルのステップ対ヒストグラムのステップの行のグラフです。 グラフ3

上下に動き始めると、ステップ数が100を下回らないことに注意してください。SQL Serverのヒストグラム作成アルゴリズムがヒストグラムのステップを結合するためのスペースが足りなくなったときに、SQL Serverのヒストグラム作成アルゴリズムが結合するという話をどこかから聞いたことがあります(ただし、現時点では入手できません)。したがって、少しのデータを追加するだけで、ステップ数が大幅に変更される可能性があります。これが私が興味深いと思ったデータのサンプルです。

ROWS_IN_TABLE   ROWS_SAMPLED    STEPS
36661           36661           133
36662           36662           143
36663           36663           143
36664           36664           141
36665           36665           138

を使用してサンプリングする場合でもFULLSCAN、1つの行を追加すると、ステップ数が10増加し、一定に保たれ、次に2減少し、次に3減少します。

これらすべてから何を要約できますか?私はこれを証明することはできませんが、これらの観察は当てはまるようです:

  • SQL Serverは、汎用アルゴリズムを使用してヒストグラムを作成します。一部のデータ配布では、手作業でデータのより完全な表現を作成できる場合があります。
  • テーブルにNULLデータがあり、統計クエリがそれを見つけた場合、そのNULLデータは常に独自のヒストグラムステップを取得します。
  • テーブルにある最小値は、RANGE_ROWS= 0の独自のヒストグラムステップを取得します。
  • 表にある最大値は、表の最後の値RANGE_HI_KEYになります。
  • SQL Serverはより多くのデータをサンプリングするため、既存の手順を組み合わせて、検出した新しいデータのためのスペースを確保する必要がある場合があります。十分なヒストグラムを見ると、DISTINCT_RANGE_ROWSまたはで繰り返される一般的な値が表示される場合がありますRANGE_ROWS。たとえば、255は、ここでの最後のテストケースRANGE_ROWSとそのDISTINCT_RANGE_ROWSための一連の時間を示しています。
  • 単純なデータ分布の場合、SQL Serverは順次データを1つのヒストグラムステップに結合し、情報の損失を引き起こさないことがわかります。ただし、データにギャップを追加すると、ヒストグラムが希望どおりに調整されない場合があります。

これらすべてが問題になるのはいつですか?これは、クエリオプティマイザーが適切な決定を行うための方法でデータ分布を表すことができないヒストグラムが原因で、クエリのパフォーマンスが低い場合に問題になります。SQL Serverが数百万行以上のヒストグラムを生成するが、正確に200または201のヒストグラムステップを使用しない場合は、ヒストグラムステップを増やす方が常に良いと思う傾向があると思います。ただし、ヒストグラムのステップ数が200または201の場合でも、統計の問題はたくさんあります。SQL Serverが統計オブジェクトに対して生成するヒストグラムステップの数を制御することはできないため、心配する必要はありません。ただし、統計の問題が原因でクエリのパフォーマンスが低下した場合に実行できる手順がいくつかあります。非常に簡単な概要を説明します。

完全な統計を収集すると、場合によっては役立つことがあります。非常に大きなテーブルの場合、自動サンプルサイズはテーブルの行の1%未満になる場合があります。場合によっては、カラム内のデータの中断によっては、計画が悪くなる可能性があります。MicrosoftのCREATE STATISTICSおよびUPDATE STATISTICSに関するドキュメントには、次のように記載されています。

SAMPLEは、デフォルトのサンプリングに基づくクエリプランが最適でない特別な場合に役立ちます。高品質のクエリプランを作成するために必要な場合、クエリオプティマイザーは既にサンプリングを使用しており、デフォルトで統計的に有意なサンプルサイズを決定するため、ほとんどの場合、SAMPLEを指定する必要はありません。

ほとんどのワークロードでは、フルスキャンは必要なく、デフォルトのサンプリングで十分です。ただし、さまざまなデータ分布の影響を受けやすい特定のワークロードでは、サンプルサイズを大きくするか、フルスキャンを行う必要があります。

場合によっては、フィルタリングされた統計を作成すると役立つことがあります。歪んだデータとさまざまな異なる値を持つ列がある場合があります。一般的にフィルタリングされるデータに特定の値がある場合、それらの一般的な値のみの統計ヒストグラムを作成できます。クエリオプティマイザーは、すべての列値で定義された統計の代わりに、より狭い範囲のデータで定義された統計を使用できます。ヒストグラムで200ステップを取得することは保証されていませんが、1つの値のみでフィルターされた統計を作成すると、ヒストグラムはその値をステップします。

パーティションビューを使用することは、テーブルに対して200を超えるステップを効果的に取得する1つの方法です。大きなテーブルを1年に1つのテーブルに簡単に分割できるとします。UNION ALL年次テーブルをすべて組み合わせたビューを作成します。各テーブルには独自のヒストグラムがあります。SQL Server 2014で導入された新しい増分統計では、統計の更新がより効率的になることにのみ注意してください。クエリオプティマイザーは、パーティションごとに作成される統計情報を使用しません。

ここで実行できるテストは他にもたくさんあるので、実験することをお勧めします。SQL Server 2014 Expressでこのテストを行ったので、本当に止めるものはありません。


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