Pertimbangkan kueri berikut yang menyisipkan baris dari tabel sumber hanya jika mereka belum ada di tabel target:
INSERT INTO dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (TABLOCK)
SELECT maybe_new_rows.ID
FROM dbo.A_HEAP_OF_MOSTLY_NEW_ROWS maybe_new_rows
WHERE NOT EXISTS (
SELECT 1
FROM dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR halloween
WHERE maybe_new_rows.ID = halloween.ID
)
OPTION (MAXDOP 1, QUERYTRACEON 7470);
Satu bentuk rencana yang mungkin termasuk gabungan gabung dan spool bersemangat. Operator spool bersemangat hadir untuk memecahkan Masalah Halloween :
Di komputer saya, kode di atas dijalankan sekitar 6900 ms. Kode repro untuk membuat tabel disertakan di bagian bawah pertanyaan. Jika saya tidak puas dengan kinerja, saya mungkin akan mencoba memuat baris yang akan dimasukkan ke dalam tabel temp bukannya mengandalkan eool spool. Inilah satu kemungkinan implementasi:
DROP TABLE IF EXISTS #CONSULTANT_RECOMMENDED_TEMP_TABLE;
CREATE TABLE #CONSULTANT_RECOMMENDED_TEMP_TABLE (
ID BIGINT,
PRIMARY KEY (ID)
);
INSERT INTO #CONSULTANT_RECOMMENDED_TEMP_TABLE WITH (TABLOCK)
SELECT maybe_new_rows.ID
FROM dbo.A_HEAP_OF_MOSTLY_NEW_ROWS maybe_new_rows
WHERE NOT EXISTS (
SELECT 1
FROM dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR halloween
WHERE maybe_new_rows.ID = halloween.ID
)
OPTION (MAXDOP 1, QUERYTRACEON 7470);
INSERT INTO dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (TABLOCK)
SELECT new_rows.ID
FROM #CONSULTANT_RECOMMENDED_TEMP_TABLE new_rows
OPTION (MAXDOP 1);
Kode baru dijalankan sekitar 4400 ms. Saya bisa mendapatkan rencana aktual dan menggunakan Statistik Waktu Aktual ™ untuk memeriksa di mana waktu dihabiskan di tingkat operator. Perhatikan bahwa meminta paket aktual menambah overhead signifikan untuk kueri ini sehingga total tidak akan cocok dengan hasil sebelumnya.
╔═════════════╦═════════════╦══════════════╗
║ operator ║ first query ║ second query ║
╠═════════════╬═════════════╬══════════════╣
║ big scan ║ 1771 ║ 1744 ║
║ little scan ║ 163 ║ 166 ║
║ sort ║ 531 ║ 530 ║
║ merge join ║ 709 ║ 669 ║
║ spool ║ 3202 ║ N/A ║
║ temp insert ║ N/A ║ 422 ║
║ temp scan ║ N/A ║ 187 ║
║ insert ║ 3122 ║ 1545 ║
╚═════════════╩═════════════╩══════════════╝
Paket kueri dengan spool eager tampaknya menghabiskan lebih banyak waktu secara signifikan pada operator insert dan spool dibandingkan dengan paket yang menggunakan tabel temp.
Mengapa rencana dengan tabel temp lebih efisien? Bukankah spool yang bersemangat kebanyakan hanya meja temp internal? Saya percaya saya mencari jawaban yang fokus pada internal. Saya dapat melihat bagaimana tumpukan panggilan berbeda tetapi tidak dapat memahami gambaran besarnya.
Saya menggunakan SQL Server 2017 CU 11 jika seseorang ingin tahu. Berikut ini adalah kode untuk mengisi tabel yang digunakan dalam permintaan di atas:
DROP TABLE IF EXISTS dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR;
CREATE TABLE dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR (
ID BIGINT NOT NULL,
PRIMARY KEY (ID)
);
INSERT INTO dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (TABLOCK)
SELECT TOP (20000000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
CROSS JOIN master..spt_values t3
OPTION (MAXDOP 1);
DROP TABLE IF EXISTS dbo.A_HEAP_OF_MOSTLY_NEW_ROWS;
CREATE TABLE dbo.A_HEAP_OF_MOSTLY_NEW_ROWS (
ID BIGINT NOT NULL
);
INSERT INTO dbo.A_HEAP_OF_MOSTLY_NEW_ROWS WITH (TABLOCK)
SELECT TOP (1900000) 19999999 + ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;