Mengapa indeks pencarian saya dapat memperkirakan jumlah baris yang tepat dan operator sortir tidak bisa?

11

Saya memiliki kueri yang menggunakan fungsi pada predikat, seperti ini:

commentType = 'EL'
AND commentDateTime >= DATEADD(month,datediff(month,0,getdate()) - 13,0)

Saya memiliki indeks yang difilter pada commentType yang memiliki baris 40K dan ketika saya menjalankan kueri, perkiraan jumlah baris untuk Index Seek sangat akurat (sekitar 11K), tetapi untuk langkah berikutnya (operator sortir) itu benar-benar mengabaikan statistik dan hanya perkirakan jumlah total baris dalam indeks yang difilter.

Mengapa ini terjadi? Saya tahu dasar-dasar tentang sargability , dan saya telah menguji hanya demi kewarasan mengganti dateadd dengan tanggal aktual (2014-01-01) dan voila ... Semacam itu mulai menebak jumlah baris dengan benar ...

Mengapa ini terjadi dan bagaimana saya bisa memperbaikinya? Saya tidak bisa melewati tanggal yang pasti ...

MrKudz
sumber
DATEADD(month,datediff(month,0,getdate()) - 13,0)tidak masuk akal bagi saya. Apa yang kamu coba lakukan dengan ini? Bisakah itu diperbaiki / disederhanakan?
Daniel Hutmacher
2
@Aniel Itu awal bulan, 13 bulan lalu.
Aaron Bertrand
1
Juga, harap edit pertanyaan Anda untuk mencerminkan versi SQL Server (?) Yang Anda gunakan. Gunakan tag untuk itu.
Daniel Hutmacher
Bisakah Anda mencoba DATEADD(month, -13, DATEADD(day, 1-DATEPART(day, SYSDATETIME()))dan melihat apakah ada perbedaan?
Daniel Hutmacher
Jika Anda memiliki indeks yang (commentType, commentDate)tidak difilter , apakah berperilaku lebih baik di sana? Hanya saja, indeks yang difilter terkadang dapat salah melaporkan perkiraan pada titik yang berbeda dalam paket. Perkiraan itu tampaknya jalan keluar dengan melaporkan jumlah total dalam indeks yang difilter, tetapi sebenarnya rencana itu ditampilkan salah.
Rob Farley

Jawaban:

9

Saya percaya perkiraan Anda salah karena bug penaksir yang menukar dua argumen DATEDIFF. Saya membicarakan hal ini di sini:

Solusinya adalah menghitung hari pertama 13 bulan yang lalu tanpa menggunakan DATEDIFF (2008+):

DATEADD(MONTH, -13, DATEADD(DAY, 1-DATEPART(DAY,GETDATE()), CONVERT(DATE, GETDATE()));

Saya tidak yakin akan membahas perkiraan (saya belum menguji dengan indeks yang difilter, dan saya tidak yakin apa yang sebenarnya dilakukan atau mengapa ia memiliki perkiraan yang berbeda tanpa rencana dan / atau sisa kueri ).

Perbaikan yang disarankan Microsoft adalah menggunakan TF 4199, tapi saya tidak begitu yakin itulah yang perlu Anda lakukan di sini:

Opsi lain adalah untuk memastikan Anda berada pada SP / CU terbaru mutlak untuk versi SQL Server apa pun yang Anda gunakan, karena mereka mengklaim itu diperbaiki dalam artikel KB berikut (meskipun ini masih akan memerlukan penggunaan TF 4199 kecuali Anda berada di 2014 atau lebih baik):

Perbaikan dapat diperoleh dengan build berikut:

(Lain kali, harap sertakan hasil SELECT @@VERSIONdalam pertanyaan Anda.)

Saya akan perhatikan bahwa artikel KB mengatakan bahwa DATEIFF dapat meremehkan jumlah baris, yang merupakan kebalikan dari apa yang terjadi dalam skenario Anda. Itu tidak berarti perbaikan tidak berlaku untuk Anda; Saya pikir kata-kata artikel KB tidak akurat, karena perkiraannya bisa tergantung pada data dan rentang yang Anda lihat.

Posting blog saya di atas mengkonfirmasi bahwa pertukaran tidak lagi terjadi pada tahun 2014 ke atas. Agar aman, saya mungkin hanya akan menghapus DATEIFF dari predikat Anda dan menggunakan metode yang berbeda untuk menghitung awal rentang Anda. Saya tidak menyarankan berlebihan dari 4199 atau menggunakan SQL dinamis untuk mencegah pertukaran yang buruk.

Aaron Bertrand
sumber
Terima kasih untuk bantuannya ! Saya mencoba saran Anda dan rencananya berubah. Beginilah sebelumnya: s16.postimg.org/t5j6o1yed/fix_wrong.png Ini adalah bagaimana setelah saya mengubah tanggal saya dengan Anda: postimg.org/image/5f725rj83 Saya akan membaca semua url yang Anda berikan kepada saya . Bersulang.
MrKudz