web-dev-qa-db-ja.com

統計と行推定

SQL Serverが統計を使用してレコード数を推定する方法について、誰かが私を助けてくれることを願っています。

テストスクリプト

USE [tempdb]

GO

CREATE TABLE t1
(
a INT NOT NULL, 
b INT NOT NULL, 
c INT CHECK (c between 1 and 50), 
CONSTRAINT pk_a primary key(a)
);

GO

INSERT INTO t1(a,b,c) 
SELECT number, number%1000+1, number%50+1 
FROM master..spt_values 
WHERE type = 'P' AND number BETWEEN 1 and 1000;

GO

CREATE STATISTICS s_b ON t1(b);
CREATE STATISTICS s_c ON t1(c);

GO

サンプルクエリ

DECLARE @c INT=300 
SELECT * FROM t1 WHERE b>@c 
SELECT * FROM t1 WHERE b>300

クエリ1

推定行数300

クエリ2

推定行数700

質問

  1. なぜ1番目と2番目のクエリ行の見積もりに大きな違いがあるのですか。
  2. 2番目のクエリSQLが統計から700に推定する方法
  3. これらを学ぶための良い記事。
5
user1716729

最初のクエリは Parameter Embedding Optimization の恩恵を受けないため、クエリは異なる推定値を示します。オプティマイザの観点からは、クエリも次のようになります。

SELECT * FROM t1 WHERE b>???; 

単純なクエリでカーディナリティの見積もりがどのように機能するかを理解するために、見積もりを作成するためにクエリオプティマイザーをプログラムする必要がある場合にどうするかについて考えることがあります。多くの場合、それらの考えはSQL Serverの動作と完全には一致しませんが、オプティマイザはほとんどの場合不完全または限られた情報を処理していることを覚えておくと役立ちます。最初のクエリに戻りましょう。

DECLARE @c INT=300; 
SELECT * FROM t1 WHERE b>@c; 

RECOMPILEヒントがないと、オプティマイザは@cローカル変数の値を認識しません。適切な見積もりを行うのに十分な情報はここにはありませんが、オプティマイザはすべてのクエリの見積もりを作成する必要があります。では、オプティマイザのプログラマは何ができるでしょうか?列の値の分布に基づいて推測を行う高度なアルゴリズムは実装可能だと思いますが、Microsoftは未知の不等式に対して30%のハードコードされた推測を使用しています。これは、ローカル変数の値を変更することで確認できます。

DECLARE @c INT=999999999; 
SELECT * FROM t1 WHERE b>@c; 

推定値はまだ300行です。推定値はクエリ間で一貫している必要はないことに注意してください。次のクエリにも、1000-300 = 700ではなく、300行の推定があります。

DECLARE @c INT=300; 
SELECT * FROM t1 WHERE NOT (b>@c); 

概念的には、次のように書き直すことができます

DECLARE @c INT=300; 
SELECT * FROM t1 WHERE b<=@c; 

同じハードコードされた30%の見積もりがあります。

2番目のクエリはローカル変数を使用しないため、オプティマイザーは列に作成した統計オブジェクトを直接使用して、推定を支援できます。ヒストグラムに含めることができるのは最大201ステップであり、テーブルには1000の異なる値があるため、オプティマイザが完全な情報を持たないフィルタ値があります。それらのケースでは、推測をさらに行う必要があります。 b列のヒストグラムを表示する方法は次のとおりです。

DBCC SHOW_STATISTICS ('t1', 's_b') WITH HISTOGRAM;

私のマシンでは、300がヒストグラムのいずれかのステップの高い値であることで幸運になりました。

histogram

さまざまな列の説明は documentation にあります。今後は、それらの意味をすべて理解していると想定します。クエリはb > 300を含む行を要求するため、RANGE_HI_KEYが300のヒストグラムは、RANGE_HI_KEYが小さいすべてのステップとは無関係です。このクエリでは、ヒストグラムの残りの133ステップのRANGE_ROWS値とEQ_ROWS値をすべて合計するようにオプティマイザをプログラムします。これらの列の合計は700になり、これはSQL Serverの見積もりです。

他のフィルター値では正確な結果が得られない場合があります。たとえば、次の2つのクエリのカーディナリティ推定値はどちらも704行です。

SELECT * FROM t1 WHERE b>293; -- returns 707 rows
SELECT * FROM t1 WHERE b>299; -- returns 701 rows

両方の見積もりは非常に近いですが、正確ではありません。ヒストグラムには、これらの値を正確に推定するのに十分な情報が含まれていません。

5
Joe Obbish

コミュニティwikiの回答

最初のケースについては、Q&A: SQLサーバーが統計の自動作成をオフに設定してクエリプランを生成する方法 を参照してください。 variableを使用するため、未知の入力と見なされ、テーブルのカーディナリティの30%(1000行の30%)と推定されます。

2番目のケースでは、統計ヒストグラムを使用します。 Nelson Johnによる SQL Server:Part 2:All About SQL Server Statistics:Histogram でそれらについて学ぶことができます。

また、@c1の現在の値に基づいて新しいプランを強制的にコンパイルすると、パラメーター化されたクエリの700行の推定値が得られます。例えば:

SELECT * FROM t1 WHERE b>@c OPTION (RECOMPILE);
1
user126897