Die Dokumentation ist etwas irreführend. Die DMV ist eine nicht materialisierte Ansicht und hat keinen Primärschlüssel als solchen. Die zugrunde liegenden Definitionen sind etwas komplex, aber eine vereinfachte Definition von sys.query_store_plan
ist:
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;
Weiter sys.plan_persist_plan_merged
ist auch eine Ansicht, obwohl man eine Verbindung über die dedizierte Administratorverbindung herstellen muss, um ihre Definition zu sehen. Wieder vereinfacht:
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;
Die Indizes sys.plan_persist_plan
sind:
╔════════════════════════╦════════════════════════ ══════════════╦═════════════╗
║ Indexname ║ Indexbeschreibung ║ Indexschlüssel ║
╠════════════════════════╬════════════════════════ ══════════════╬═════════════╣
║ plan_persist_plan_cidx ║ gruppiert, eindeutig auf PRIMARY ║ plan_id ║
║ plan_persist_plan_idx1 ║ nicht gruppiert auf PRIMARY ║ query_id (-) ║
╚════════════════════════╩════════════════════════ ══════════════╩═════════════╝
Ist plan_id
also gezwungen, einzigartig zu sein sys.plan_persist_plan
.
Jetzt sys.plan_persist_plan_in_memory
handelt es sich um eine Streaming-Tabellenfunktion, die eine tabellarische Ansicht von Daten darstellt, die nur in internen Speicherstrukturen gespeichert sind. Als solches hat es keine eindeutigen Einschränkungen.
Im Kern entspricht die ausgeführte Abfrage daher:
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;
... was keine Join-Eliminierung bewirkt:
Um zum Kern des Problems zu gelangen, ist das Problem die innere Frage:
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;
... eindeutig kann die linke Verknüpfung dazu führen, dass Zeilen @t2
dupliziert werden, da @t3
keine Eindeutigkeitsbeschränkung besteht plan_id
. Daher kann der Join nicht entfernt werden:
Um dies zu umgehen, können wir dem Optimierer ausdrücklich mitteilen, dass wir keine doppelten plan_id
Werte benötigen :
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;
Die äußere Verbindung zu @t3
kann jetzt entfernt werden:
Anwenden auf die eigentliche Abfrage:
SELECT DISTINCT
T.plan_id
FROM #tears AS T
LEFT JOIN sys.query_store_plan AS QSP
ON QSP.plan_id = T.plan_id;
Ebenso könnten wir GROUP BY T.plan_id
anstelle der hinzufügen DISTINCT
. Auf plan_id
jeden Fall kann das Optimierungsprogramm das Attribut jetzt korrekt durch die verschachtelten Ansichten führen und beide äußeren Verknüpfungen wie gewünscht entfernen:
Beachten Sie, dass eine plan_id
eindeutige Angabe in der temporären Tabelle nicht ausreicht, um eine Join-Eliminierung zu erzielen, da dies keine falschen Ergebnisse ausschließt. Wir müssen doppelte plan_id
Werte aus dem Endergebnis explizit ablehnen , damit der Optimierer hier seine Magie entfalten kann.