왜 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 ║
╠ ==================================================== ================ ╬ =============== ╣
IM plan_persist_plan_cidx ║ 클러스터에 있으며 고유 한 위치는 PRIMARY ║ plan_id ║
IM 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.