tempdbイベントへの流出(遅いクエリの原因)が発生すると、特定の結合で行の見積もりがずれることがよくあります。マージイベントとハッシュ結合で流出イベントが発生し、ランタイムが3倍から10倍に増えることがよくあります。この質問は、流出事故の可能性を減らすことを前提として、行の見積もりを改善する方法に関係しています。
行の実際の数40k。
このクエリの場合、プランは不適切な行の見積もり(11.3行)を示しています。
select Value
from Oav.ValueArray
where ObjectId = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
このクエリの場合、プランは適切な行推定(56k行)を示しています。
declare @a bigint = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2);
select Value
from Oav.ValueArray
where ObjectId = @a
and PropertyId = 2840
option (recompile);
最初のケースの行推定を改善するために、統計またはヒントを追加できますか?特定のフィルター値(プロパティ= 2840)で統計を追加しようとしましたが、組み合わせを正しく取得できなかったか、コンパイル時にObjectIdが不明であり、すべてのObjectIdの平均を選択している可能性があるため、無視されている可能性があります。
最初にプローブクエリを実行し、それを使用して行の推定値を決定するモードや、盲目的に飛行するモードはありますか?
この特定のプロパティには、少数のオブジェクトでは多くの値(40k)があり、大多数ではゼロです。特定の結合の予想される最大行数を指定できるというヒントに満足しています。一部のパラメータは結合の一部として動的に決定されるか、ビュー内に配置する方がよい(変数のサポートなし)ため、これは一般的に悩ましい問題です。
tempdbへの流出の可能性を最小限に抑えるために調整できるパラメーターはありますか(たとえば、クエリごとの最小メモリ)。堅牢な計画は、見積もりに影響を与えませんでした。
2013.11.06を編集:コメントおよび追加情報への応答:
クエリプランの画像は次のとおりです。警告は、convert()のカーディナリティ/シーク述語に関するものです。
@Aaron Bertrandのコメントに従って、テストとしてconvert()を置き換えてみました。
create table Oav.SeekObject (
LookupId bigint not null primary key,
ObjectId bigint not null
);
insert into Oav.SeekObject (
LookupId, ObjectId
) VALUES (
1, 3540233
)
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.SeekObject
where LookupId = 1)
and PropertyId = 2840
option (recompile);
奇妙だが成功した興味深い点として、ルックアップを短絡させることもできました:
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.ValueArray
where PropertyId = 2840
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
これらは両方とも適切なキールックアップをリストしますが、最初のものだけがObjectIdの「出力」をリストします。2番目が確かに短絡であることを示していると思いますか?
誰かが行推定を支援するために単一行プローブが実行されたかどうかを確認できますか?単一行のPKルックアップでヒストグラムへのルックアップの精度を大幅に向上できる場合(特に、流出の可能性や履歴がある場合)、最適化をヒストグラム推定のみに制限するのは間違っているようです。実際のクエリにこれらのサブジョインが10個ある場合、理想的にはそれらは並行して発生します。
余談ですが、sql_variantはその基本型(SQL_VARIANT_PROPERTY = BaseType)をフィールド自体に格納するため、「直接」変換可能(たとえば、文字列を10進数に変換するのではなく、intからint intまたは多分intからbigint)。コンパイル時にはわかりませんが、ユーザーにはわかる場合があるため、sql_variantsの「AssumeType(type、...)」関数を使用すると、透過的に処理できるようになります。
declare @a bigint =
あなたが行ったように使用してクエリを分割することは私にとって自然な解決策のようですが、なぜそれは受け入れられないのですか?
CONVERT()
、列で使用してから結合することを強制していると思います。これは確かにほとんどの場合効率的ではありません。この特定のものでは、変換される値は1つだけなので、それはおそらく問題ではありませんが、テーブルにはどのインデックスがありますか?EAV設計は通常、適切なインデックス付け(つまり、通常は狭いテーブルに多数のインデックスが作成される)を使用した場合にのみ、適切に機能します。