该文档有点误导。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
最终结果中的重复值,以使优化器在这里发挥作用。