Ini agak luas tapi saya pikir saya mengerti Pertanyaan Sejati dan akan menjawabnya. Hanya akan berbicara tentang tabel vs indeks spool. Saya tidak berpikir itu benar untuk melihat di sana sebagai pilihan antara tabel dan spool indeks. Seperti yang Anda ketahui, dimungkinkan dalam subtree tunggal untuk mendapatkan spool indeks, spool tabel, atau keduanya spool indeks dan spool tabel. Saya percaya secara umum benar untuk mengatakan bahwa Anda mendapatkan spool indeks dalam kondisi berikut:
- Pengoptimal kueri memiliki alasan untuk mengubah suatu sambungan menjadi suatu penerapan
- Pengoptimal kueri benar-benar melakukan transformasi untuk menerapkan
- Pengoptimal kueri menggunakan aturan untuk menambahkan spool indeks (minimal spool indeks harus aman untuk digunakan)
- Paket dengan spool indeks dipilih
Anda dapat melihat sebagian besar dari ini dengan demo sederhana. Mulailah dengan membuat sepasang tumpukan:
DROP TABLE IF EXISTS dbo.X_10000_VARCHAR_901;
CREATE TABLE dbo.X_10000_VARCHAR_901 (ID VARCHAR(901) NOT NULL);
INSERT INTO dbo.X_10000_VARCHAR_901 WITH (TABLOCK)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
DROP TABLE IF EXISTS dbo.X_10000_VARCHAR_800;
CREATE TABLE dbo.X_10000_VARCHAR_800 (ID VARCHAR(800) NOT NULL);
INSERT INTO dbo.X_10000_VARCHAR_800 WITH (TABLOCK)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
Untuk kueri pertama, tidak ada yang dicari:
SELECT *
FROM dbo.X_10000_VARCHAR_901 a
CROSS JOIN dbo.X_10000_VARCHAR_901 b
OPTION (MAXDOP 1);
Jadi tidak ada alasan bagi pengoptimal untuk mengubah bergabung menjadi berlaku. Anda berakhir dengan gulungan meja karena alasan penetapan biaya. Jadi kueri ini gagal pada tes pertama.
Untuk kueri berikutnya, wajar berharap bahwa pengoptimal memiliki alasan untuk mempertimbangkan penerapan:
SELECT *
FROM dbo.X_10000_VARCHAR_901 a
INNER JOIN dbo.X_10000_VARCHAR_901 b ON a.ID = b.ID
OPTION (LOOP JOIN, MAXDOP 1);
Tapi itu tidak dimaksudkan sebagai:
Permintaan ini gagal pada pengujian kedua. Penjelasan lengkap ada di sini . Mengutip bagian yang paling relevan:
Pengoptimal tidak mempertimbangkan membangun indeks dengan cepat untuk memungkinkan penerapan; alih-alih urutan kejadian biasanya adalah kebalikannya: transformasi untuk diterapkan karena indeks yang baik ada.
Saya dapat menulis ulang kueri untuk mendorong pengoptimal mempertimbangkan penerapan:
SELECT *
FROM dbo.X_10000_VARCHAR_901 a
INNER JOIN dbo.X_10000_VARCHAR_901 b ON a.ID >= b.ID AND a.ID <= b.ID
OPTION (MAXDOP 1);
Tapi masih belum ada spool indeks:
Kueri ini gagal pada pengujian ketiga. Di SQL Server 2014 ada batas panjang kunci indeks 900 byte. Ini diperluas di SQL Server 2016 tetapi hanya untuk indeks yang tidak dibatasi. Indeks untuk spool adalah indeks berkerumun sehingga batasnya tetap pada 900 byte . Dalam kasus apa pun, aturan spool indeks tidak dapat diterapkan karena dapat menyebabkan kesalahan selama eksekusi permintaan.
Mengurangi panjang tipe data menjadi 800 akhirnya menyediakan paket dengan spool indeks:
Rencana spool indeks, tidak mengherankan, dihitung biayanya secara signifikan lebih murah daripada rencana tanpa spool: 89.7603 unit vs 598.832 unit. Anda dapat melihat perbedaannya dengan QUERYRULEOFF BuildSpool
petunjuk permintaan tidak berdokumen :
Ini bukan jawaban yang lengkap, tapi mudah-mudahan ini adalah sebagian dari apa yang Anda cari.