結合消去がsys.query_store_planで機能しないのはなぜですか?


10

以下は、クエリストアで発生するパフォーマンスの問題の簡略化です。

CREATE TABLE #tears
(
    plan_id bigint NOT NULL
);

INSERT #tears (plan_id) 
VALUES (1);

SELECT
    T.plan_id
FROM #tears AS T
LEFT JOIN sys.query_store_plan AS QSP
    ON QSP.plan_id = T.plan_id;

plan_id列は、主キーとして文書化されているsys.query_store_planが、実行計画は使用されません撤廃への参加が期待されるように:

  1. DMVから投影される属性はありません。
  2. DMV主キーplan_idは一時テーブルの行を複製できません
  3. A LEFT JOINが使用されているため、から行をT削除できません。

実行計画

計画グラフィック

これはなぜですか、ここで参加の削除を取得するにはどうすればよいですか?

回答:


16

ドキュメントは少し誤解を招くものです。DMVは非具体化ビューであり、そのような主キーはありません。基礎となる定義は少し複雑ですが、以下の簡略化された定義sys.query_store_planは次のとおりです。

CREATE VIEW sys.query_store_plan AS
SELECT
    PPM.plan_id
    -- various other attributes
FROM sys.plan_persist_plan_merged AS PPM
LEFT JOIN sys.syspalvalues AS P
    ON P.class = 'PFT' 
    AND P.[value] = plan_forcing_type;

さらに、sys.plan_persist_plan_mergedもビューですが、その定義を確認するには専用管理者接続を介して接続する必要があります。再度、簡略化しました:

CREATE VIEW sys.plan_persist_plan_merged AS   
SELECT     
    P.plan_id as plan_id,    
    -- various other attributes
FROM sys.plan_persist_plan P 
    -- NOTE - in order to prevent potential deadlock
    -- between QDS_STATEMENT_STABILITY LOCK and index locks   
    WITH (NOLOCK) 
LEFT JOIN sys.plan_persist_plan_in_memory PM
    ON P.plan_id = PM.plan_id;

インデックスsys.plan_persist_planは次のとおりです。

╔════════════════════════╦════════════════════════ ══════════════╦═════════════╗
║index_name║index_description║index_keys║
╠════════════════════════╬════════════════════════ ══════════════╬═════════════╣
║plan_persist_plan_cidx║クラスタ化された一意のPRIMARYにあります║plan_id║
║plan_persist_plan_idx1 PR PRIMARYにある非クラスター║query_id(-)║
╚════════════════════════╩════════════════════════ ══════════════╩═════════════╝

したがってplan_id、はで一意になるように制約されていsys.plan_persist_planます。

これsys.plan_persist_plan_in_memoryはストリーミングテーブル値関数であり、内部メモリ構造にのみ保持されているデータを表形式で表示します。そのため、固有の制約はありません。

したがって、実行されるクエリは、本質的には次と同等です。

DECLARE @t1 table (plan_id integer NOT NULL);
DECLARE @t2 table (plan_id integer NOT NULL UNIQUE CLUSTERED);
DECLARE @t3 table (plan_id integer NULL);

SELECT 
    T1.plan_id
FROM @t1 AS T1 
LEFT JOIN
(
    SELECT 
        T2.plan_id
    FROM @t2 AS T2
    LEFT JOIN @t3 AS T3 
        ON T3.plan_id = T2.plan_id
) AS Q1
    ON Q1.plan_id = T1.plan_id;

...これは結合の削除を生成しません:

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

問題の核心を正しく理解すると、問題は内部クエリです。

DECLARE @t2 table (plan_id integer NOT NULL UNIQUE CLUSTERED);
DECLARE @t3 table (plan_id integer NULL);

SELECT 
    T2.plan_id
FROM @t2 AS T2
LEFT JOIN @t3 AS T3 
    ON T3.plan_id = T2.plan_id;

...に一意性の制約がない@t2ため、左結合では行が重複する可能@t3性がありますplan_id。したがって、結合は削除できません。

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

これを回避するために、重複するplan_id値を必要としないことをオプティマイザに明示的に伝えることができます。

DECLARE @t2 table (plan_id integer NOT NULL UNIQUE CLUSTERED);
DECLARE @t3 table (plan_id integer NULL);

SELECT DISTINCT
    T2.plan_id
FROM @t2 AS T2
LEFT JOIN @t3 AS T3 
    ON T3.plan_id = T2.plan_id;

への外部結合を@t3削除できるようになりました。

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

それを実際のクエリに適用する:

SELECT DISTINCT
    T.plan_id
FROM #tears AS T
LEFT JOIN sys.query_store_plan AS QSP
    ON QSP.plan_id = T.plan_id;

同様に、のGROUP BY T.plan_id代わりに追加できDISTINCTます。とにかく、オプティマイザplan_idはネストされたビュー全体にわたって属性について正しく推論できるようになり、必要に応じて両方の外部結合を削除できます。

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

plan_id一時テーブルで一意にすることは、誤った結果を排除しないため、結合の排除を行うのに十分ではないことに注意してください。plan_idここでオプティマイザがその魔法を働かせるには、最終結果からの重複値を明示的に拒否する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.