为什么不能使用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消除中的任何行。

执行计划

平面图

为什么会这样?在这里如何做才能消除连接?

Answers:


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上唯一,unique plan_id║
║plan_persist_plan_idx1║非聚集位于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最终结果中的重复值,以使优化器在这里发挥作用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.