Saya memiliki masalah ini sejak lama, saya menemukan solusi yang cocok untuk saya dan melupakannya.
Tapi sekarang ada pertanyaan tentang SO jadi saya bersedia mengangkat masalah ini.
Ada tampilan yang menggabungkan beberapa tabel dengan cara yang sangat mudah (pesanan + garis pemesanan).
Ketika ditanya tanpa where
klausa, tampilan mengembalikan beberapa juta baris.
Namun, tidak ada yang pernah memanggilnya seperti itu. Permintaan yang biasa adalah
select * from that_nasty_view where order_number = 123456;
Ini mengembalikan sekitar 10 catatan dari 5m.
Suatu hal yang penting: tampilan berisi fungsi jendela rank()
,, yang dipartisi persis oleh bidang yang selalu digunakan tampilan:
rank() over (partition by order_number order by detail_line_number)
Sekarang, jika tampilan ini ditanya dengan parameter literal dalam string kueri, persis seperti yang ditunjukkan di atas, ia mengembalikan baris secara instan. Rencana eksekusi baik-baik saja:
- Pencarian indeks pada kedua tabel menggunakan indeks on
order_number
(menghasilkan 10 baris). - Menghitung jendela di atas hasil kecil yang dikembalikan.
- Memilih.
Namun, ketika tampilan dipanggil dengan cara parametrized, semuanya menjadi buruk:
Index scan
di semua tabel mengabaikan indeks. Mengembalikan baris 5m.- Bergabung besar.
- Menghitung windows di semua
partition
s (sekitar 500k windows). Filter
untuk mengambil 10 baris dari 5m.- Pilih
Ini terjadi dalam semua kasus ketika parameter terlibat. Itu bisa SSMS:
declare @order_number int = 123456;
select * from that_nasty_view where order_number = @order_number;
Itu bisa menjadi klien ODBC, seperti Excel:
select * from that_nasty_view where order_number = ?
Atau bisa juga klien lain yang menggunakan parameter dan bukan gabungan sql.
Jika fungsi jendela dihapus dari tampilan, itu berjalan dengan sangat cepat, terlepas dari apakah itu dipertanyakan dengan parameter atau tidak.
Solusi saya adalah menghapus fungsi yang menyinggung dan menerapkannya kembali pada tahap selanjutnya.
Tapi, apa yang terjadi? Apakah ini benar-benar bug dalam cara SQL Server 2008 menangani fungsi jendela?
order_number
bukan kunci utama. Ituint not null
dengan indeks nonclustered di kedua tabel.OPTION (RECOMPILE)
bantuan?Jawaban:
Ini tampaknya menjadi masalah lama yang terus muncul kembali dalam satu bentuk atau yang lain dan masih ada di SQL Server 2012.
Beberapa postingan membahasnya
Semua versi SQL Server saat ini hingga dan termasuk 2012 tidak dapat mendorong filter pada grup partisi melewati proyek urutan untuk predikat parameter kecuali jika
option(recompile)
digunakan (jika 2008+).Alternatif dari
recompile
petunjuk itu adalah menulis ulang kueri untuk menggunakan TVF sebaris parameter seperti yang disarankan oleh @ a1ex07)sumber
Saya akan mencoba mengganti tampilan dengan udf bernilai tabel. Dengan begitu ia akan memfilter catatan pertama, dan kemudian menerapkan fungsi jendela. Fungsi ini dapat menerima parameter tabel sehingga Anda dapat mengirim beberapa
order_number
ke dalamnyasumber
SELECT * FROM my_funct(12345)
it will filter records first, and then apply window function
salah. Tidak ada perintah deterministik untuk eksekusi