Spooling Pindai Konstan

14

Saya punya meja dengan beberapa baris. Setup yang disederhanakan mengikuti

CREATE TABLE #data ([Id] int, [Status] int);

INSERT INTO #data
VALUES (100, 1), (101, 2), (102, 3), (103, 2);

Dan saya punya kueri yang menggabungkan tabel ini ke satu set nilai tabel yang dibangun baris (terbuat dari variabel dan konstanta), seperti

DECLARE @id1 int = 101, @id2 int = 105;

SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
        (@id1, 'A'),
        (@id2, 'B')
    ) p([Id], [Code])
    FULL JOIN #data d ON d.[Id] = p.[Id];

Rencana pelaksanaan kueri menunjukkan bahwa keputusan pengoptimal adalah menggunakan FULL LOOP JOINstrategi, yang tampaknya tepat, karena kedua input memiliki sangat sedikit baris. Satu hal yang saya perhatikan (dan tidak bisa setuju), adalah baris TVC sedang digulung (lihat area rencana eksekusi di kotak merah).

Spooling Pindai Konstan

Mengapa optimizer memperkenalkan spool di sini, apa alasan untuk melakukannya? Tidak ada yang kompleks di luar gulungan. Sepertinya itu tidak perlu. Bagaimana cara menghilangkannya dalam hal ini, apa saja cara yang mungkin?


Rencana di atas diperoleh pada

Microsoft SQL Server 2014 (SP2-CU11) (KB4077063) - 12.0.5579.0 (X64)

i-satu
sumber
Saran terkait di feedback.azure.com
i-one

Jawaban:

19

Mengapa optimizer memperkenalkan spool di sini, apa alasan untuk melakukannya? Tidak ada yang kompleks di luar gulungan.

Hal di luar spool bukanlah referensi tabel sederhana, yang dapat dengan mudah diduplikasi ketika alternatif join / anti semi join dihasilkan.

Mungkin terlihat sedikit seperti tabel (Pemindaian Konstan) tetapi untuk pengoptimal * ini adalah UNION ALLbaris terpisah dalam VALUESklausa.

Kompleksitas tambahan sudah cukup bagi pengoptimal untuk memilih spool dan memutar ulang baris sumber, dan tidak mengganti spool dengan "tabel get" sederhana nanti. Misalnya, transformasi awal dari gabungan penuh terlihat seperti ini:

rencana awal

Perhatikan gulungan ekstra yang diperkenalkan oleh transformasi umum. Gulungan di atas meja sederhana dibersihkan kemudian oleh aturanSpoolGetToGet .

Jika pengoptimal memiliki SpoolConstGetToConstGetaturan yang sesuai , prinsipnya bisa berfungsi sesuai keinginan.

Bagaimana cara menghilangkannya dalam hal ini, apa saja cara yang mungkin?

Gunakan tabel nyata (sementara atau variabel), atau tulis transformasi dari gabungan lengkap secara manual, misalnya:

WITH 
    p([Id], [Code]) AS
    (
        SELECT @id1, 'A'
        UNION ALL
        SELECT @id2, 'B'
    ),
    FullJoin AS
    (
        SELECT
            p.Code,
            d.[Status]
        FROM p
        LEFT JOIN #data d 
            ON d.[Id] = p.[Id]
        UNION ALL
        SELECT
            NULL,
            D.[Status]
        FROM #data AS D
        WHERE NOT EXISTS
        (
            SELECT *
            FROM p
            WHERE p.Id = D.Id
        )
    )
SELECT
    COALESCE(FullJoin.Code, 'X') AS Code,
    COALESCE(FullJoin.Status, 0) AS [Status]
FROM FullJoin;

Rencanakan penulisan ulang manual:

Rencana penulisan ulang manual

Ini memiliki perkiraan biaya 0,0067201 unit, dibandingkan dengan 0,0203412 unit untuk aslinya.


* Dapat diamati sebagai LogOp_UnionAlldalam Pohon Konversi (TF 8605). Di Pohon Input (TF 8606) itu adalah a LogOp_ConstTableGet. The Dikonversi Pohon menunjukkan pohon elemen ekspresi optimizer setelah parsing, normalisasi, algebrization, mengikat, dan beberapa persiapan lainnya. The Masukan Pohon menunjukkan unsur-unsur setelah konversi ke Negasi Bentuk Normal (NNF convert), runtime konstan runtuh, dan beberapa lainnya bit dan bobs. Konversi NNF mencakup logika untuk menutup persatuan logis dan tabel umum, antara lain.

Paul White 9
sumber
3

Spool tabel hanya membuat tabel dari dua set tupel yang ada dalam VALUESklausa.

Anda bisa menghilangkan spool dengan memasukkan nilai-nilai tersebut ke tabel temp terlebih dahulu, seperti:

DROP TABLE IF EXISTS #data;
CREATE TABLE #data ([Id] int, [Status] int);

INSERT INTO #data
VALUES (100, 1), (101, 2), (102, 3), (103, 2);

DROP TABLE IF EXISTS #p;
CREATE TABLE #p
(
    Id int NOT NULL
    , Code char(1) NOT NULL
);

DECLARE @id1 int = 101, @id2 int = 105;

INSERT INTO #p (Id, Code)
VALUES
        (@id1, 'A'),
        (@id2, 'B');


SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM #p p
    FULL JOIN #data d ON d.[Id] = p.[Id];

Melihat rencana eksekusi untuk permintaan Anda, kami melihat daftar output berisi dua kolom yang menggunakan Unionawalan; ini adalah petunjuk bahwa spool membuat tabel dari sumber union'd:

masukkan deskripsi gambar di sini

Itu FULL OUTER JOIN membutuhkan SQL Server untuk mengakses nilai dalam pdua kali, sekali untuk setiap "sisi" dari bergabung. Membuat spul memungkinkan loop batin yang dihasilkan bergabung untuk mengakses data spool.

Menariknya, jika Anda mengganti FULL OUTER JOINdengan a LEFT JOINdan a RIGHT JOIN, dan UNIONhasilnya bersama-sama, SQL Server tidak menggunakan spool.

SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
        (101, 'A'),
        (105, 'B')
    ) p([Id], [Code])
    LEFT JOIN #data d ON d.[Id] = p.[Id]
UNION
SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
        (101, 'A'),
        (105, 'B')
    ) p([Id], [Code])
    RIGHT JOIN #data d ON d.[Id] = p.[Id];

masukkan deskripsi gambar di sini

Catatan, saya tidak menyarankan menggunakan UNIONkueri di atas; untuk set input yang lebih besar, itu mungkin tidak lebih efisien daripada yang sederhana FULL OUTER JOINyang sudah Anda miliki.

Max Vernon
sumber
Dalam beban kerja Anda yang sebenarnya, apakah spool benar-benar mahal?
Max Vernon