La documentazione è un po 'fuorviante. Il DMV è una vista non materializzata e non ha una chiave primaria in quanto tale. Le definizioni sottostanti sono un po 'complesse ma una definizione semplificata di 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;
Inoltre, sys.plan_persist_plan_merged
è anche una vista, anche se è necessario connettersi tramite la Connessione amministratore dedicata per vedere la sua definizione. Ancora una volta, semplificato:
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;
Gli indici su sys.plan_persist_plan
sono:
╔════════════════════════╦════════════════════════ ══════════════╦═════════════╗
║ nome_indice ║ descrizione_indice ║ tasti_indice ║
╠════════════════════════╬════════════════════════ ══════════════╬═════════════╣
║ plan_persist_plan_cidx ║ cluster, unico situato su PRIMARY ║ plan_id ║
║ plan_persist_plan_idx1 ║ non cluster situato su PRIMARY ║ query_id (-) ║
╚════════════════════════╩════════════════════════ ══════════════╩═════════════╝
Quindi plan_id
è costretto a essere unico sys.plan_persist_plan
.
Ora, sys.plan_persist_plan_in_memory
è una funzione con valori di tabella di streaming, che presenta una vista tabellare dei dati contenuti solo nelle strutture di memoria interna. Come tale, non ha vincoli univoci.
In sostanza, la query in esecuzione è quindi 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;
... che non produce eliminazione dei join:
Giungendo al nocciolo del problema, il problema è la query 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;
... chiaramente il join sinistro potrebbe comportare la @t2
duplicazione delle righe perché @t3
non ha vincoli di unicità plan_id
. Pertanto, il join non può essere eliminato:
Per ovviare a questo, possiamo dire esplicitamente all'ottimizzatore che non abbiamo bisogno di plan_id
valori duplicati :
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;
Il join esterno su @t3
può ora essere eliminato:
Applicandolo alla query reale:
SELECT DISTINCT
T.plan_id
FROM #tears AS T
LEFT JOIN sys.query_store_plan AS QSP
ON QSP.plan_id = T.plan_id;
Allo stesso modo, potremmo aggiungere GROUP BY T.plan_id
invece di DISTINCT
. Ad ogni modo, l'ottimizzatore ora può ragionare correttamente sull'attributo plan_id
fino in fondo attraverso le viste nidificate ed eliminare entrambi i join esterni come desiderato:
Notare che rendere plan_id
univoco nella tabella temporanea non sarebbe sufficiente per ottenere l'eliminazione del join, poiché non precluderebbe risultati errati. Dobbiamo rifiutare esplicitamente i plan_id
valori duplicati dal risultato finale per consentire all'ottimizzatore di fare la sua magia qui.