Документація трохи вводить в оману. 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
:
╔════════════════════════╦════════════════════════ ══════════════╦═════════════╗
Name index_name ║ index_description ║ index_keys ║
╠════════════════════════╬════════════════════════ ══════════════╬═════════════╣
║ plan_persist_plan_cidx ║ кластеризований, унікальний, розміщений на PRIMARY ║ 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
значення від кінцевого результату, щоб оптимізатор міг працювати тут свою магію.