連結演算子が入力よりも少ない行を推定するのはなぜですか?


20

次のクエリプランスニペットでは、Concatenation演算子の行推定値はである必要があること~4.3 billion rows、またはその2つの入力の行推定値の合計であることは明らかです。

しかし、の推定値は~238 million rows、最適につながる、生成されるSort/ Stream AggregatetempdbのにデータのGB数百をこぼし戦略。この場合の論理的に一貫した推定は、を生成しHash Aggregate、流出を除去し、クエリパフォーマンスを劇的に改善します。

これはSQL Server 2014のバグですか?入力よりも低い見積もりが合理的である有効な状況はありますか?どのような回避策が利用可能ですか?

ここに画像の説明を入力してください

ここで完全なクエリプラン(匿名化)が。出力QUERYTRACEON 2363または類似のトレースフラグを提供するためにこのサーバーにsysadminアクセスすることはできませんが、役立つ場合は管理者からこれらの出力を取得できる場合があります。

データベースは互換性レベル120にあるため、新しいSQL Server 2014 Cardinality Estimatorを使用しています。

統計は、データがロードされるたびに手動で更新されます。データ量を考えると、現在デフォルトのサンプリングレートを使用しています。より高いサンプリングレート(またはFULLSCAN)が影響を与える可能性があります。

回答:


21

このConnectアイテムで Campbell Fraserを引用するには:

これらの「カーディナリティの不一致」は、concatが使用される場合を含め、多くの状況で発生する可能性があります。最終計画の特定のサブツリーの推定が、異なる構造であるが論理的に同等なサブツリーで実行された可能性があるため、それらが発生する可能性があります。カーディナリティ推定の統計的性質により、異なるが論理的に等価なツリーでの推定は、同じ推定を取得することを保証されません。したがって、予想される一貫性の全体的な保証は提供されません。

それを少し拡張するには:最初のカーディナリティの推定(コストベースの最適化が始まる前に実行される)は、初期ツリー全体が処理され、後続の各先行するものに直接依存する推定。

コストベースの最適化中に、プランツリーの一部(1つまたは複数の演算子)を調査し、それぞれに新しいカーディナリティの推定値必要になる可能性のある代替物に置き換えることができます。一般に、どの推定値が他の推定値よりも優れているかを示す一般的な方法はないため、最終的に「一貫性のない」計画になる可能性があります。これは、単に「計画の一部」をつなぎ合わせて最終的な配置を形成した結果です。

とはいえ、SQL Server 2014で導入された新しいカーディナリティーエスティメータ(CE)には、元のCEの場合よりもやや一般的ではない、いくつかの詳細な変更がありました。

最新の累積更新にアップグレードし、4199でのオプティマイザーの修正がオンになっていることを確認する以外に、主なオプションは、統計/インデックスの変更(インデックスの欠落に関する警告に注意)と更新を試すか、クエリを異なる方法で表現することです。目標は、必要な動作を表示する計画を取得することです。これは、たとえばプランガイドで凍結できます。

匿名化されたプランでは詳細を評価するのが難しくなりますが、ビットマップを注意深く見て、それらが「最適化された」(Opt_Bitmap)または最適化後(ビットマップ)の種類であるかどうかを確認します。私もフィルターを疑っています。

ただし、行カウントが正確に近い場合、これは列ストアの恩恵を受ける可能性のあるクエリのようです。通常の利点とは別に、バッチモード演算子の動的メモリ許可を利用できる場合があります(トレースフラグ9389が必要な場合があります)。


7

SQL Server 2012(11.0.6020)にかなり単純なテストベッドを構築すると、2つのハッシュ一致クエリがを介して連結されたプランを再作成できますUNION ALL。私のテストベッドには、間違った見積もりが表示されません。おそらくこれ SQL Server 2014 CEの問題です。

実際に280行を返すクエリに対して133.785行の推定値が得られますが、これについては後で詳しく説明します。

IF OBJECT_ID('dbo.Union1') IS NOT NULL
DROP TABLE dbo.Union1;
CREATE TABLE dbo.Union1
(
    Union1_ID INT NOT NULL
        CONSTRAINT PK_Union1
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , Union1_Text VARCHAR(255) NOT NULL
    , Union1_ObjectID INT NOT NULL
);

IF OBJECT_ID('dbo.Union2') IS NOT NULL
DROP TABLE dbo.Union2;
CREATE TABLE dbo.Union2
(
    Union2_ID INT NOT NULL
        CONSTRAINT PK_Union2
        PRIMARY KEY CLUSTERED
        IDENTITY(2,2)
    , Union2_Text VARCHAR(255) NOT NULL
    , Union2_ObjectID INT NOT NULL
);

INSERT INTO dbo.Union1 (Union1_Text, Union1_ObjectID)
SELECT o.name, o.object_id
FROM sys.objects o;

INSERT INTO dbo.Union2 (Union2_Text, Union2_ObjectID)
SELECT o.name, o.object_id
FROM sys.objects o;
GO

SELECT *
FROM dbo.Union1 u1
    INNER HASH JOIN sys.objects o ON u1.Union1_ObjectID = o.object_id
UNION ALL
SELECT *
FROM dbo.Union2 u2
    INNER HASH JOIN sys.objects o ON u2.Union2_ObjectID = o.object_id;

理由は、UNIONになっている2つの結果の結合の統計情報の不足にあると思います。SQL Serverは、ほとんどの場合、統計の欠如に直面したときの列の選択性に関する知識に基づいた推測を行う必要があります。

ジョーサックの興味深い記事がここにあります

aのUNION ALL場合、ユニオンの各コンポーネントによって返された行の合計数を正確に表示すると言っても安全ですが、SQL Serverはの2つのコンポーネントに対して行推定を使用しているため、両方のUNION ALL合計推定行を追加することがわかります連結演算子の推定値を求めるクエリ。

上記の私の例では、の各部分の推定行数UNION ALLは66.8927であり、合計すると133.785になり、連結演算子の推定行数で見ることができます。

上記のユニオンクエリの実際の実行計画は次のようになります。

ここに画像の説明を入力してください

「推定」対「実際」の行数を確認できます。私の場合、2つのハッシュ一致演算子によって返される「推定」数の行を追加すると、連結演算子によって示される量と正確に等しくなります。

質問で示したPaul Whiteの投稿で推奨されているように、トレース2363などから出力を取得しようとします。あるいは、OPTION (QUERYTRACEON 9481)クエリでを使用してバージョン70 CE戻り、問題が「修正」されるかどうかを確認することもできます。


1
ありがとう。「理由は、UNIONになった2つの結果の結合の統計が不足していることです」が、後続の結合または集約(UNIONの後に発生する)に大きな影響を与えることは間違いありません。私の経験では、SQL 2014はSQL 2012よりも実際にこれをうまく処理しています。ここで私は、たとえば過去に使用した簡単なテストスクリプトです:gist.github.com/anonymous/1497112d8b25ab8fb782a04569959c68は しかし、私は、連結演算子が参加することを値の分布に関する情報と同じ種類のを必要とするだろうとは思いませんでしょう必要かもしれません。
ジェフパターソン

連結で、正確に実行するために統計を必要としないことに同意します。入力される行の推定値を簡単に追加して、出力する行の数を正確に把握できる必要があります。@PaulWhiteが彼の答えで示しているように、驚くほどいつもそうではない。私にとってここでのポイントは、シンプルに見えるかもしれませんが、実際にはそうではないかもしれません。質問にあなたがしたように質問してくれて本当にうれしいです。私はあなたが計画を匿名化する必要がないことを願っています-実際のクエリを見るのは面白かったでしょう。
マックスヴァーノン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.