La documentación es un poco engañosa. El DMV es una vista no materializada y no tiene una clave principal como tal. Las definiciones subyacentes son un poco complejas, pero una definición simplificada de sys.query_store_plan
es:
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;
Además, sys.plan_persist_plan_merged
también es una vista, aunque es necesario conectarse a través de la Conexión de administrador dedicada para ver su definición. De nuevo, simplificado:
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;
Los índices en sys.plan_persist_plan
son:
╔════════════════════════╦════════════════════════ ══════════════╦═════════════╗
║ index_name ║ index_description ║ index_keys ║
╠════════════════════════╬════════════════════════ ══════════════╬═════════════╣
║ plan_persist_plan_cidx ║ agrupado, ubicado en PRIMARY ║ plan_id ║
║ plan_persist_plan_idx1 ║ no agrupado ubicado en PRIMARY ║ query_id (-) ║
╚════════════════════════╩════════════════════════ ══════════════╩═════════════╝
Por plan_id
lo tanto, está obligado a ser único en sys.plan_persist_plan
.
Ahora, sys.plan_persist_plan_in_memory
es una función de transmisión de valores de tabla, que presenta una vista tabular de los datos que solo se encuentran en las estructuras de la memoria interna. Como tal, no tiene restricciones únicas.
En el fondo, la consulta que se ejecuta es, por lo tanto, equivalente a:
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;
... que no produce eliminación conjunta:
Llegando directamente al núcleo del problema, el problema es la consulta interna:
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;
... claramente, la unión izquierda puede hacer que las filas @t2
se dupliquen porque @t3
no tiene restricción de unicidad plan_id
. Por lo tanto, la unión no se puede eliminar:
Para solucionar esto, podemos decirle explícitamente al optimizador que no requerimos ningún plan_id
valor duplicado :
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;
La unión externa a @t3
ahora se puede eliminar:
Aplicando eso a la consulta real:
SELECT DISTINCT
T.plan_id
FROM #tears AS T
LEFT JOIN sys.query_store_plan AS QSP
ON QSP.plan_id = T.plan_id;
Igualmente, podríamos agregar en GROUP BY T.plan_id
lugar de DISTINCT
. De todos modos, el optimizador ahora puede razonar correctamente sobre el plan_id
atributo a través de las vistas anidadas y eliminar ambas uniones externas como desee:
Tenga en cuenta que hacer plan_id
único en la tabla temporal no sería suficiente para obtener la eliminación de la unión, ya que no excluiría resultados incorrectos. Debemos rechazar explícitamente plan_id
valores duplicados del resultado final para permitir que el optimizador haga su magia aquí.