Mengapa tidak bergabung dengan eliminasi bekerja dengan sys.query_store_plan?

10

Berikut ini adalah penyederhanaan masalah kinerja yang dihadapi dengan Query Store:

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;

The plan_idkolom didokumentasikan sebagai kunci utama sys.query_store_plan, tapi rencana eksekusi tidak menggunakan bergabung eliminasi seperti yang diharapkan:

  1. Tidak ada atribut yang diproyeksikan dari DMV.
  2. Kunci utama DMV plan_idtidak dapat menduplikasi baris dari tabel sementara
  3. A LEFT JOINdigunakan, jadi tidak ada baris dari Tdapat dihilangkan.

Rencana eksekusi

grafik rencana

Mengapa ini, dan apa yang bisa dilakukan untuk mendapatkan penghapusan bergabung di sini?

Paul White 9
sumber

Jawaban:

16

Dokumentasinya sedikit menyesatkan. DMV adalah pandangan yang tidak terwujud, dan tidak memiliki kunci primer seperti itu. Definisi yang mendasarinya sedikit rumit tetapi definisi yang disederhanakan sys.query_store_planadalah:

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;

Lebih lanjut, sys.plan_persist_plan_mergedini juga merupakan pandangan, meskipun seseorang harus terhubung melalui Koneksi Administrator Khusus untuk melihat definisinya. Sekali lagi, disederhanakan:

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;

Indeks yang aktif sys.plan_persist_planadalah:

╔════════════════════════╦════════════════════════ ══════════════╦═════════════╗
║ index_name ║ index_description ║ index_keys ║
╠════════════════════════╬════════════════════════ ══════════════╬═════════════╣
║ plan_persist_plan_cidx ║ berkerumun, unik terletak di PRIMARY ║ plan_id ║
║ plan_persist_plan_idx1 ║ nonclustered terletak di PRIMARY ║ query_id (-) ║
╚════════════════════════╩════════════════════════ ══════════════╩═════════════╝

Jadi plan_iddibatasi untuk menjadi unik sys.plan_persist_plan.

Sekarang, sys.plan_persist_plan_in_memoryadalah fungsi streaming yang dihargai tabel, menyajikan tampilan tabular data yang hanya disimpan dalam struktur memori internal. Karena itu, tidak memiliki kendala unik.

Karena itu, kueri yang dijalankan setara dengan:

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;

... yang tidak menghasilkan eliminasi gabungan:

masukkan deskripsi gambar di sini

Langsung ke inti masalah, masalahnya adalah kueri batin:

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;

... jelas gabung kiri mungkin menghasilkan baris dari @t2duplikasi karena @t3tidak memiliki kendala keunikan plan_id. Karenanya, join tidak dapat dihilangkan:

masukkan deskripsi gambar di sini

Untuk mengatasinya, kami dapat secara eksplisit memberi tahu pengoptimal bahwa kami tidak memerlukan plan_idnilai duplikat :

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;

Gabung luar ke @t3sekarang dapat dihilangkan:

masukkan deskripsi gambar di sini

Menerapkannya ke permintaan sebenarnya:

SELECT DISTINCT
    T.plan_id
FROM #tears AS T
LEFT JOIN sys.query_store_plan AS QSP
    ON QSP.plan_id = T.plan_id;

Sama, kita bisa menambahkan GROUP BY T.plan_idbukan DISTINCT. Pokoknya, pengoptimal sekarang dapat dengan benar alasan tentang plan_idatribut sepanjang jalan melalui tampilan bersarang, dan menghilangkan kedua gabungan luar yang diinginkan:

masukkan deskripsi gambar di sini

Perhatikan bahwa membuat plan_idunik di tabel sementara tidak akan cukup untuk mendapatkan eliminasi gabungan, karena itu tidak akan menghalangi hasil yang salah. Kami harus secara eksplisit menolak plan_idnilai duplikat dari hasil akhir untuk memungkinkan pengoptimal bekerja dengan baik di sini.

Paul White 9
sumber