Mengapa kueri ini tidak menggunakan spool indeks?

23

Saya mengajukan pertanyaan ini untuk lebih memahami perilaku pengoptimal dan untuk memahami batas-batas di sekitar gulungan indeks. Misalkan saya menaruh bilangan bulat dari 1 hingga 10.000 ke dalam tumpukan:

CREATE TABLE X_10000 (ID INT NOT NULL);
truncate table X_10000;

INSERT INTO X_10000 WITH (TABLOCK)
SELECT TOP 10000 ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

Dan paksa loop bersarang bergabung dengan MAXDOP 1:

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID = b.ID
OPTION (LOOP JOIN, MAXDOP 1);

Ini adalah tindakan yang agak tidak ramah untuk dilakukan terhadap SQL Server. Nested loop bergabung sering bukan pilihan yang baik ketika kedua tabel tidak memiliki indeks yang relevan. Inilah rencananya:

permintaan buruk

Permintaan membutuhkan waktu 13 detik pada mesin saya dengan 10.0000000 baris diambil dari tabel spool. Namun, saya tidak melihat mengapa permintaan harus lambat. Pengoptimal kueri memiliki kemampuan untuk membuat indeks dengan cepat melalui gulungan indeks . Kueri ini sepertinya akan menjadi kandidat yang sempurna untuk spool indeks.

Kueri berikut mengembalikan hasil yang sama dengan yang pertama, memiliki spool indeks, dan selesai dalam waktu kurang dari satu detik:

SELECT *
FROM X_10000 a
CROSS APPLY (SELECT TOP (9223372036854775807) b.ID FROM X_10000 b WHERE a.ID = b.ID) ca
OPTION (LOOP JOIN, MAXDOP 1);

solusi 1

Kueri ini juga memiliki spool indeks dan selesai dalam waktu kurang dari satu detik:

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID >= b.ID AND a.ID <= b.ID
OPTION (LOOP JOIN, MAXDOP 1);

solusi 2

Mengapa kueri asli tidak memiliki spool indeks? Apakah ada set petunjuk atau jejak bendera yang didokumentasikan atau tidak berdokumen yang akan memberikan indeks spool? Saya memang menemukan pertanyaan terkait ini , tetapi itu tidak sepenuhnya menjawab pertanyaan saya dan saya tidak bisa mendapatkan tanda jejak misterius untuk bekerja untuk permintaan ini.

Joe Obbish
sumber

Jawaban:

20

Seperti yang Anda ketahui, pencarian pengoptimal tidak lengkap. Ini mencoba hal-hal yang masuk akal dalam konteks, dan yang sering membayar dividen pada permintaan nyata. Memaksa loop bergabung antara dua tabel tumpukan kolom unindexed tunggal bukan skenario seperti itu. Yang mengatakan, berikut adalah beberapa detail:

SQL Server suka mengubah berlaku untuk bergabung lebih awal, karena ia tahu lebih banyak trik dengan bergabung. Di kemudian hari, itu mungkin mengeksplorasi mengubah bergabung kembali ke mendaftar. The perbedaan antara dua parameter makhluk berkorelasi (referensi luar). Berlaku masuk akal ketika ada indeks yang sesuai di sisi dalam. Contoh Anda tidak memiliki indeks, sehingga pengoptimal tidak dibujuk untuk menjelajahi terjemahan untuk diterapkan.

Gabung sederhana (tidak berlaku) memiliki predikat gabung pada operator gabung alih-alih referensi luar. Optimasi spool untuk non-apply biasanya spool table malas, karena tidak ada predikat di sisi dalam, hanya di join.

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.

Anda kadang-kadang dapat mendorong aplikasi daripada bergabung dengan menggunakan APPLYsintaks dalam permintaan Anda. Bendera jejak yang tidak didokumentasikan 9114 dapat membantu dalam hal ini dengan menghalangi pengoptimal agar tidak menerjemahkan penerapan logis ke bergabung di muka. Sebagai contoh:

SELECT * 
FROM dbo.X_1000 AS a
CROSS APPLY (SELECT * FROM dbo.X_1000 AS b WHERE b.ID = a.ID) AS b
OPTION (QUERYTRACEON 9114);

Paket spool

Spool indeks disukai untuk diterapkan karena referensi luar berarti pemilihan diterapkan pada sisi bagian dalam sambungan. Anda akan sering melihat ini melalui SelToIndexOnTheFlytetapi jalur lain ada. Lihat artikel saya Spool Indeks Eager dan Pengoptimal .

Paul White mengatakan GoFundMonica
sumber