次のクエリプランスニペットでは、Concatenation
演算子の行推定値は~4.3 billion rows
、またはその2つの入力の行推定値の合計であることは明らかです。
ただし、推定~238 million rows
が生成され、次善のSort
/Stream Aggregate
戦略により、数百GBのデータがtempdbに流出します。この場合の論理的に一貫した見積もりはHash Aggregate
を生成し、流出を除去し、クエリのパフォーマンスを劇的に改善します。
これはSQL Server 2014のバグですか?入力よりも低い見積もりが妥当である可能性のある有効な状況はありますか?利用可能な回避策はありますか?
これが 完全なクエリプラン (匿名化)です。 QUERYTRACEON 2363
または類似のトレースフラグからの出力を提供するために、このサーバーへのsysadminアクセス権はありませんが、役立つ場合は、管理者からこれらの出力を取得できる場合があります。
データベースは互換性レベル120であるため、新しいSQL Server 2014 Cardinality Estimatorを使用しています。
統計は、データが読み込まれるたびに手動で更新されます。データ量を考慮して、現在デフォルトのサンプリングレートを使用しています。高いサンプリングレート(またはFULLSCAN
)が影響を与える可能性があります。
この接続項目 でキャンベルフレイザーを引用するには:
これらの「カーディナリティーの不整合」は、concatが使用されている場合など、さまざまな状況で発生する可能性があります。最終計画の特定のサブツリーの推定が、構造が異なるが論理的に同等のサブツリーで実行された可能性があるために発生する可能性があります。カーディナリティー推定の統計的性質により、異なるが論理的に同等のツリーでの推定は、同じ推定が得られるとは限りません。したがって、全体として期待される一貫性の保証は提供されません。
それを少し拡張します:私が説明したいのは、initialカーディナリティ推定(コストベースの最適化が始まる前に実行される)が生成するということです初期ツリー全体が処理され、後続の各推定は前のものに直接依存するため、より「一貫した」カーディナリティ推定。
コストベースの最適化中に、計画ツリーの一部(1つ以上の演算子)を探索し、代替案で置き換えることができます。それぞれの代替案はmay新しい基数の見積もり。どの見積もりが他の見積もりよりも一般的に優れていると言う一般的な方法はないため、最終的に「一貫性がない」ように見える最終計画になる可能性があります。これは単に「計画の一部」をつなぎ合わせて最終的な配置を形成した結果です。
そうは言っても、SQL Server 2014で導入された新しいカーディナリティエスティメータ(CE)にはいくつかの詳細な変更があり、これによりsomewhatが通常よりも少なくなっています元のCE。
最新の累積的な更新にアップグレードし、4199でのオプティマイザーの修正がオンになっていることを確認する以外に、主なオプションは、統計/インデックスの変更(インデックスの欠落に関する警告に注意)と更新を試すか、クエリを別の方法で表現することです。目標は、必要な動作を表示する計画を取得することです。これは、たとえば計画ガイドで凍結できます。
匿名化された計画では詳細を評価することが困難になりますが、ビットマップを注意深く調べて、それらが「最適化された」(Opt_Bitmap)か、最適化後(Bitmap)の種類であるかを確認します。フィルターにも疑いがあります。
ただし、行数が正確である場合、これは列ストアの恩恵を受けるクエリのようです。通常のメリットとは別に、バッチモードオペレーターの動的メモリ許可を利用できる場合があります( トレースフラグ9389 が必要になる場合があります)。
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;
_
Ithink理由は、UNIONされた2つの結合の統計が不足していることです。 SQL Serverは、統計の欠如に直面した場合、ほとんどの場合、列の選択性に関して知識のある推測を行う必要があります。
Joe Sackは、その興味深い記事を読んでいます here 。
_UNION ALL
_の場合、ユニオンの各コンポーネントによって返される行の総数が正確に表示されると言っても安全ですが、SQL Serverは行の推定値を使用しているため _UNION ALL
_の2つのコンポーネントについては、両方のクエリからの合計estimated行を追加して、連結演算子の推定値を導き出します。
上記の例では、_UNION ALL
_の各部分の推定行数は66.8927で、合計すると133.785になります。これは、連結演算子の推定行数でわかります。
上記のユニオンクエリの実際の実行プランは次のようになります。
「推定」対「実際の」行数を確認できます。私の場合、2つのハッシュ一致演算子によって返された「推定」数の行を追加すると、連結演算子によって示される量と正確に等しくなります。
質問に示したPaul Whiteの投稿で推奨されているように、トレース2363などから出力を取得しようとします。または、クエリでOPTION (QUERYTRACEON 9481)
を使用して バージョン70 CEに戻す を試して、問題が「修正」されるかどうかを確認することもできます。