Pau*_*ite 10 performance join sql-server execution-plan query-store query-performance
以下是查询存储遇到的性能问题的简化:
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;
Run Code Online (Sandbox Code Playgroud)
该plan_id列被记录为 的主键sys.query_store_plan,但执行计划并不像预期的那样使用连接消除:
plan_id不能复制临时表中的行LEFT JOIN,因此无法T消除from中的任何行。为什么会这样,在这里可以做些什么来消除连接?
Pau*_*ite 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;
Run Code Online (Sandbox Code Playgroud)
此外,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;
Run Code Online (Sandbox Code Playgroud)
上的索引sys.plan_persist_plan是:
?????????????????????????????????????????????????????? ????????????????????????????????? ? 索引名称?索引描述?索引键? ?????????????????????????????????????????????????????? ????????????????????????????????? ? plan_persist_plan_cidx ? 聚集的,独特的位于 PRIMARY 上?计划 ID ? ? plan_persist_plan_idx1 ?非聚集位于 PRIMARY 上?query_id(-) ? ?????????????????????????????????????????????????????? ?????????????????????????????????
Soplan_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;
Run Code Online (Sandbox Code Playgroud)
...这不会产生连接消除:
直击问题的核心,问题在于内部查询:
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;
Run Code Online (Sandbox Code Playgroud)
...显然,左连接可能会导致行@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;
Run Code Online (Sandbox Code Playgroud)
@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;
Run Code Online (Sandbox Code Playgroud)
同样,我们可以添加GROUP BY T.plan_id而不是DISTINCT. 无论如何,优化器现在可以正确地推理plan_id整个嵌套视图中的属性,并根据需要消除两个外部连接:
请注意,使plan_id临时表中的唯一性不足以消除连接,因为它不会排除错误结果。我们必须明确拒绝plan_id最终结果中的重复值,以允许优化器在这里发挥其魔力。
| 归档时间: |
|
| 查看次数: |
386 次 |
| 最近记录: |