これらの同様のクエリが異なる最適化フェーズ(トランザクション処理とクイックプラン)を使用するのはなぜですか?


12

この接続アイテムのサンプルコード

バグを示します

SELECT COUNT(*)
FROM   dbo.my_splitter_1('2') L1
       INNER JOIN dbo.my_splitter_1('') L2
         ON L1.csv_item = L2.csv_item

正しい結果を返します。ただし、次の例では誤った結果が返されます(2014年、新しいCardinality Estimatorを使用)

SELECT
    (SELECT COUNT(*)
    FROM dbo.my_splitter_1('2') L1
     INNER JOIN dbo.my_splitter_1('') L2
        ON L1.csv_item = L2.csv_item)

L2の結果が共通のサブ式スプールに誤ってロードされ、L1の結果の結果が再生されるためです。

2つのクエリの動作の違いがなぜなのか興味がありました。トレースフラグ8675は、動作search(0) - transaction processingするものが入り、失敗するものが入っていることを示していますsearch(1) - quick plan

したがって、追加の変換ルールの可用性は動作の違いの背後にあると考えられます(BuildGbApplyまたはGenGbApplySimpleを無効にすると、たとえば修正されるようです)。

しかし、これらの非常によく似たクエリの2つの計画で、異なる最適化フェーズが発生するのはなぜですか?私が読んだことからsearch (0)、少なくとも3つのテーブルが必要であり、最初の例ではその条件は確かに満たされていません。

回答:


7

各ステージにはエントリー条件があります。「少なくとも3つのテーブル参照を持つ」は、簡単な例を挙げるときに説明するエントリ条件の1つですが、それだけではありません。

一般に、0を検索するためのエントリには基本的な結合と結合のみが許可されます。スカラーサブクエリ、セミジョインなどにより、検索0へのエントリが妨げられます。この段階は、非常に一般的なOLTPタイプのクエリ形状用です。あまり一般的ではないものを探索するために必要なルールは、有効になっていません。サンプルクエリにはスカラーサブクエリがあるため、エントリに失敗します。

また、テーブル参照のカウント方法にも依存します。関数でこれを詳しく調べたことはありませんが、ロジックがテーブル値関数とそれらが生成するテーブル変数をカウントしている可能性があります。関数自体の内部のテーブル参照をカウントすることもできます-わかりません。ただし、関数はすべての面で大変な作業であることを知っています。

バグGenGbApplySimpleはThe いです。この計画形状は常に可能性がありましたが、100行を想定したテーブル変数のカーディナリティーへの変更が発生するまで、コスト上の理由で拒否されましたUSE PLAN

新しいConnectアイテム以前に報告されたものと同じ問題であることは正しいです。

例として、次のクエリは検索0に適格です。

DECLARE @T AS table (c1 integer NULL);

SELECT U.c1, rn = ROW_NUMBER() OVER (ORDER BY U.c1) 
FROM 
(
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
) AS U;

スカラーサブクエリを含めるために小さな変更を加えると、検索1に直接進みます。

DECLARE @T AS table (c1 integer NULL);

SELECT U.c1, rn = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -- Changed!
FROM 
(
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
) AS U;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.