Fungsi-fungsi jendela menyebabkan rencana pelaksanaan yang mengerikan ketika dipanggil dari tampilan dengan klausa parametrized eksternal 'di mana'

10

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 whereklausa, 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 scandi semua tabel mengabaikan indeks. Mengembalikan baris 5m.
  • Bergabung besar.
  • Menghitung windows di semua partitions (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?

GSerg
sumber
order_number adalah kunci utama? Datatipe kolom dan parameter yang cocok?
gbn
order_numberbukan kunci utama. Itu int not nulldengan indeks nonclustered di kedua tabel.
GSerg
5
SQL Server 2005 memiliki masalah dengan pendorong mendorong di bidang ini. Saya pikir mereka sudah diperbaiki sekarang. BTW contoh TSQL Anda menggunakan variabel bukan parameter. Apakah menambah OPTION (RECOMPILE)bantuan?
Martin Smith
1
@ GSerg - Jadi pada rencana buruk dengan filter terakhir ini diperkirakan 5 juta baris ke dalam filter dan sekitar 10 baris cocok dengan yang sebenarnya? Jika demikian mungkin itu adalah masalah mendorong predikat yang masih belum diperbaiki sepenuhnya.
Martin Smith

Jawaban:

5

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 recompilepetunjuk itu adalah menulis ulang kueri untuk menggunakan TVF sebaris parameter seperti yang disarankan oleh @ a1ex07)

Martin Smith
sumber
Baru saja memiliki kasus di SQL Server 2014
Guillaume86
3

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_numberke dalamnya

a1ex07
sumber
Solusi lain, ya. Saya tidak bisa melakukan itu karena tidak semua klien dapat mengkonsumsi fungsi bernilai tabel.
GSerg
Mengapa? Saya tidak 100% yakin, tapi saya pikir yang Anda butuhkan hanyalah mengubah kueri sedikit menjadi sesuatu sepertiSELECT * FROM my_funct(12345)
a1ex07
Salah satu persyaratannya adalah kueri dapat dikonsumsi oleh pengguna akhir menggunakan Excel (yaitu, oleh MS Query), dan MS Query tidak akan membiarkan Anda melakukan itu, setidaknya dalam versi hingga 2003.
GSerg
it will filter records first, and then apply window functionsalah. Tidak ada perintah deterministik untuk eksekusi
Remus Rusanu