Чому б не приєднатися до роботи з усунення з sys.query_store_plan?


10

Далі йде спрощення проблеми з продуктивністю, що виникає із магазином запитів:

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;

plan_idКолона документована як первинний ключ sys.query_store_plan, але план виконання не використовує приєднатися до ліквідації , як можна було б очікувати:

  1. Жодні атрибути не проектуються з DMV.
  2. Первинний ключ DMV plan_idне може дублювати рядки з тимчасової таблиці
  3. A LEFT JOINвикористовується, тому жодні рядки з не Tможуть бути усунені.

План виконання

план графічний

Чому це так, і що тут можна зробити для усунення приєднання?

Відповіді:


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;

Далі, 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значення від кінцевого результату, щоб оптимізатор міг працювати тут свою магію.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.