Apakah SQL Server memungkinkan untuk mengevaluasi A <> B
sebagai A < B OR A > B
, bahkan jika salah satu ekspresi adalah non-deterministik?
Ini adalah poin yang agak kontroversial, dan jawabannya adalah "ya" yang memenuhi syarat.
Diskusi terbaik yang saya sadari diberikan sebagai jawaban atas laporan bug Itzik Ben-Gan Connect Bug dengan NEWID dan Table Expressions , yang ditutup karena tidak akan diperbaiki. Connect telah dipensiunkan, jadi tautannya ada ke arsip web. Sayangnya, banyak materi bermanfaat hilang (atau semakin sulit ditemukan) oleh matinya Connect. Pokoknya, kutipan paling berguna dari Jim Hogg dari Microsoft ada:
Ini sangat menyentuh inti permasalahan - apakah optimisasi diizinkan untuk mengubah semantik program? Yaitu: jika suatu program menghasilkan jawaban tertentu, tetapi berjalan lambat, apakah sah untuk Pengoptimal Kueri membuat program itu berjalan lebih cepat, namun juga mengubah hasil yang diberikan?
Sebelum berteriak "TIDAK!" (Kecenderungan pribadi saya sendiri juga :-), pertimbangkan: kabar baiknya adalah, dalam 99% kasus, jawabannya sama. Jadi Optimasi Kueri adalah kemenangan yang jelas. Berita buruknya adalah, jika kueri berisi kode efek samping, maka paket yang berbeda BISA menghasilkan hasil yang berbeda. Dan NEWID () adalah salah satu 'fungsi' efek samping (non-deterministik) yang memaparkan perbedaan. [Sebenarnya, jika Anda bereksperimen, Anda dapat menyusun yang lain - misalnya, evaluasi hubung singkat klausa AND: membuat klausa kedua membuang aritmatika divide-by-zero - optimasi yang berbeda dapat mengeksekusi klausa kedua SEBELUM klausa pertama] Ini mencerminkan Penjelasan Craig, di tempat lain di utas ini, bahwa SqlServer tidak menjamin ketika operator skalar dieksekusi.
Jadi, kita punya pilihan: jika kita ingin menjamin perilaku tertentu di hadapan kode non-deterministik (efek samping) - sehingga hasil GABUNGAN, misalnya, ikuti semantik dari eksekusi nested-loop - maka kita dapat menggunakan OPSI yang tepat untuk memaksa perilaku itu - seperti yang ditunjukkan UC. Tetapi kode yang dihasilkan akan berjalan lambat - itulah biaya, pada dasarnya, tertatih-tatih Pengoptimal Kueri.
Semua yang dikatakan, kami memindahkan Pengoptimal Kueri ke arah perilaku "seperti yang diharapkan" untuk NEWID () - menukar kinerja untuk "hasil seperti yang diharapkan".
Salah satu contoh perubahan perilaku dalam hal ini dari waktu ke waktu adalah NULLIF bekerja secara salah dengan fungsi-fungsi non-deterministik seperti RAND () . Ada juga kasus serupa lainnya yang digunakan misalnya COALESCE
dengan subquery yang dapat menghasilkan hasil yang tidak terduga, dan yang juga sedang ditangani secara bertahap.
Jim melanjutkan:
Menutup lingkaran. . . Saya sudah membahas pertanyaan ini dengan tim Dev. Dan akhirnya kami memutuskan untuk tidak mengubah perilaku saat ini, karena alasan berikut:
1) Pengoptimal tidak menjamin waktu atau jumlah eksekusi fungsi skalar. Ini adalah prinsip yang sudah lama ditetapkan. Ini adalah 'kelonggaran' mendasar yang memungkinkan pengoptimal kebebasan yang cukup untuk mendapatkan peningkatan yang signifikan dalam eksekusi rencana permintaan.
2) "perilaku sekali per baris" ini bukan masalah baru, meskipun tidak banyak dibahas. Kami mulai mengubah perilakunya kembali di rilis Yukon. Tetapi cukup sulit untuk dijabarkan dengan tepat, dalam semua kasus, persis apa artinya! Misalnya, apakah ini berlaku untuk baris sementara yang dihitung 'dalam perjalanan' ke hasil akhir? - dalam hal ini jelas tergantung pada rencana yang dipilih. Atau apakah itu hanya berlaku untuk baris yang pada akhirnya akan muncul dalam hasil yang selesai? - Ada rekursi jahat terjadi di sini, karena saya yakin Anda akan setuju!
3) Seperti yang saya sebutkan sebelumnya, kami default untuk "mengoptimalkan kinerja" - yang bagus untuk 99% kasus. 1% dari kasus di mana ia dapat mengubah hasil cukup mudah dikenali - 'fungsi' efek samping seperti NEWID - dan mudah untuk 'diperbaiki' (perdagangan perf, sebagai konsekuensinya). Default ini untuk "mengoptimalkan kinerja" lagi, sudah lama didirikan, dan diterima. (Ya, itu bukan sikap yang dipilih oleh kompiler untuk bahasa pemrograman konvensional, tetapi jadilah itu).
Jadi, rekomendasi kami adalah:
a) Hindari ketergantungan pada waktu yang tidak dijamin dan jumlah eksekusi semantik. b) Hindari penggunaan NEWID () ekspresi dalam-dalam tabel. c) Gunakan OPTION untuk memaksa perilaku tertentu (perdagangan perf)
Semoga penjelasan ini membantu menjelaskan alasan kami untuk menutup bug ini sebagai "tidak akan diperbaiki".
Menariknya, AND NOT (s_guid = NEWID())
menghasilkan rencana eksekusi yang sama
Ini adalah konsekuensi dari normalisasi, yang terjadi sangat awal selama kompilasi permintaan. Kedua ekspresi dikompilasi ke bentuk normal yang sama persis, sehingga rencana eksekusi yang sama dihasilkan.