Pertanyaan aslinya adalah "Bagaimana cara parameterisasi kueri ..."
Izinkan saya menyatakan di sini, bahwa ini bukan jawaban untuk pertanyaan awal. Sudah ada beberapa demonstrasi tentang hal itu di jawaban yang baik lainnya.
Dengan mengatakan itu, silakan dan tandai jawaban ini, turunkan suaranya, tandai sebagai bukan jawaban ... lakukan apa pun yang menurut Anda benar.
Lihat jawaban dari Mark Brackett untuk jawaban yang lebih disukai yang saya (dan 231 lainnya) angkat. Pendekatan yang diberikan dalam jawabannya memungkinkan 1) untuk penggunaan variabel terikat yang efektif, dan 2) untuk predikat yang lebih besar.
Jawaban yang dipilih
Yang ingin saya sampaikan di sini adalah pendekatan yang diberikan dalam jawaban Joel Spolsky, jawaban "terpilih" sebagai jawaban yang tepat.
Pendekatan Joel Spolsky cerdas. Dan itu bekerja dengan wajar, itu akan menunjukkan perilaku yang dapat diprediksi dan kinerja yang dapat diprediksi, diberi nilai "normal", dan dengan kasus tepi normatif, seperti NULL dan string kosong. Dan itu mungkin cukup untuk aplikasi tertentu.
Tetapi dalam hal menggeneralisasikan pendekatan ini, mari kita juga mempertimbangkan kasus sudut yang lebih jelas, seperti ketika Name
kolom berisi karakter wildcard (seperti yang dikenali oleh predikat LIKE). Karakter wildcard yang saya lihat paling umum digunakan adalah %
(tanda persen.). Jadi mari kita hadapi itu di sini sekarang, dan kemudian lanjutkan ke kasus lain.
Beberapa masalah dengan% karakter
Pertimbangkan nilai Nama 'pe%ter'
. (Untuk contoh di sini, saya menggunakan nilai string literal sebagai ganti nama kolom.) Baris dengan nilai Nama `'pe% ter' akan dikembalikan oleh kueri bentuk:
select ...
where '|peanut|butter|' like '%|' + 'pe%ter' + '|%'
Tapi baris yang sama itu tidak akan dikembalikan jika urutan istilah pencarian dibalik:
select ...
where '|butter|peanut|' like '%|' + 'pe%ter' + '|%'
Perilaku yang kita amati agak aneh. Mengubah urutan istilah pencarian dalam daftar akan mengubah set hasil.
Hampir tidak perlu dikatakan bahwa kita mungkin tidak ingin pe%ter
mencocokkan selai kacang, tidak peduli seberapa besar dia menyukainya.
Kasus sudut tidak jelas
(Ya, saya akan setuju bahwa ini adalah kasus yang tidak jelas. Mungkin yang tidak mungkin diuji. Kami tidak akan mengharapkan wildcard dalam nilai kolom. Kami dapat mengasumsikan bahwa aplikasi mencegah nilai disimpan dari nilai tersebut. Tetapi dalam pengalaman saya, saya jarang melihat kendala basis data yang secara khusus melarang karakter atau pola yang akan dianggap wildcard di sisi kanan LIKE
operator pembanding.
Menambal lubang
Salah satu pendekatan untuk menambal lubang ini adalah untuk melarikan diri dari %
karakter wildcard. (Untuk siapa pun yang tidak terbiasa dengan klausa melarikan diri pada operator, berikut ini tautan ke dokumentasi SQL Server .
select ...
where '|peanut|butter|'
like '%|' + 'pe\%ter' + '|%' escape '\'
Sekarang kita dapat mencocokkan% literal. Tentu saja, ketika kita memiliki nama kolom, kita harus keluar secara dinamis dari wildcard. Kita dapat menggunakan REPLACE
fungsi untuk menemukan kemunculan %
karakter dan menyisipkan karakter garis miring terbalik di depan masing-masing karakter, seperti ini:
select ...
where '|pe%ter|'
like '%|' + REPLACE( 'pe%ter' ,'%','\%') + '|%' escape '\'
Sehingga memecahkan masalah dengan% wildcard. Hampir.
Escape the escape
Kami menyadari bahwa solusi kami telah menimbulkan masalah lain. Karakter melarikan diri. Kita melihat bahwa kita juga perlu melarikan diri dari setiap kejadian karakter pelarian itu sendiri. Kali ini, kami menggunakan! sebagai karakter pelarian:
select ...
where '|pe%t!r|'
like '%|' + REPLACE(REPLACE( 'pe%t!r' ,'!','!!'),'%','!%') + '|%' escape '!'
Garis bawah juga
Sekarang kita berada di roll, kita dapat menambahkan REPLACE
pegangan lain wildcard garis bawah. Dan hanya untuk bersenang-senang, kali ini, kami akan menggunakan $ sebagai karakter pelarian.
select ...
where '|p_%t!r|'
like '%|' + REPLACE(REPLACE(REPLACE( 'p_%t!r' ,'$','$$'),'%','$%'),'_','$_') + '|%' escape '$'
Saya lebih suka pendekatan ini untuk melarikan diri karena ia bekerja di Oracle dan MySQL serta SQL Server. (Saya biasanya menggunakan \ backslash sebagai karakter pelarian, karena itulah karakter yang kami gunakan dalam ekspresi reguler. Tapi mengapa harus dibatasi oleh konvensi!
Kurung sial itu
SQL Server juga memungkinkan karakter wildcard diperlakukan sebagai literal dengan melampirkannya dalam tanda kurung []
. Jadi kita belum selesai memperbaiki, setidaknya untuk SQL Server. Karena pasangan tanda kurung memiliki arti khusus, kita juga perlu menghindarinya. Jika kita berhasil keluar dari kurung, maka setidaknya kita tidak perlu repot dengan tanda hubung -
dan karat ^
di dalam kurung. Dan kita bisa pergi%
dan _
karakter di dalam tanda kurung lolos, karena pada dasarnya kita akan menonaktifkan arti khusus tanda kurung.
Menemukan pasangan kurung yang cocok seharusnya tidak sulit. Ini sedikit lebih sulit daripada menangani kejadian singleton% dan _. (Perhatikan bahwa tidak cukup untuk hanya melarikan diri dari semua kejadian kurung, karena braket tunggal dianggap literal, dan tidak perlu diloloskan. Logikanya menjadi sedikit lebih kabur daripada yang bisa saya tangani tanpa menjalankan lebih banyak kasus uji .)
Ekspresi sebaris menjadi berantakan
Ungkapan inline dalam SQL semakin panjang dan jelek. Kita mungkin dapat membuatnya bekerja, tetapi surga membantu jiwa miskin yang datang dan harus menguraikannya. Sebanyak penggemar saya untuk ekspresi inline, saya cenderung tidak menggunakannya di sini, terutama karena saya tidak ingin harus meninggalkan komentar menjelaskan alasan kekacauan, dan meminta maaf untuk itu.
Fungsi mana?
Oke, jadi, jika kita tidak mengatasinya sebagai ekspresi inline dalam SQL, alternatif terdekat yang kita miliki adalah fungsi yang ditentukan pengguna. Dan kita tahu bahwa tidak akan mempercepat apa pun (kecuali kita dapat menentukan indeks di atasnya, seperti kita bisa dengan Oracle.) Jika kita harus membuat fungsi, kita mungkin lebih baik melakukannya dalam kode yang memanggil SQL pernyataan.
Dan fungsi itu mungkin memiliki beberapa perbedaan dalam perilaku, tergantung pada DBMS dan versi. (Teriakan untuk semua pengembang Java Anda yang ingin sekali menggunakan mesin basis data secara bergantian.)
Pengetahuan domain
Kami mungkin memiliki pengetahuan khusus tentang domain untuk kolom, (yaitu, kumpulan nilai yang diizinkan yang diberlakukan untuk kolom tersebut. Kita mungkin mengetahui apriori bahwa nilai yang disimpan dalam kolom tidak akan pernah mengandung tanda persen, garis bawah, atau braket dalam hal ini, kami hanya menyertakan komentar cepat bahwa kasus-kasus tersebut dibahas.
Nilai-nilai yang disimpan dalam kolom memungkinkan% atau _ karakter, tetapi kendala mungkin mengharuskan nilai-nilai itu untuk melarikan diri, mungkin menggunakan karakter yang ditentukan, sehingga nilai-nilai tersebut SEPERTI perbandingan "aman". Sekali lagi, komentar singkat tentang set nilai yang diperbolehkan, dan khususnya karakter mana yang digunakan sebagai karakter pelarian, dan ikuti pendekatan Joel Spolsky.
Tetapi, tanpa pengetahuan khusus dan jaminan, penting bagi kami untuk setidaknya mempertimbangkan menangani kasus sudut yang tidak jelas itu, dan mempertimbangkan apakah perilaku tersebut masuk akal dan "sesuai spesifikasi".
Masalah-masalah lain direkapitulasi
Saya percaya orang lain telah cukup menunjukkan beberapa bidang lain yang dianggap umum:
Injeksi SQL (mengambil apa yang kelihatannya merupakan informasi yang disediakan pengguna, dan memasukkannya dalam teks SQL alih-alih memasoknya melalui variabel bind. Menggunakan variabel bind tidak diperlukan, itu hanya satu pendekatan yang mudah untuk menggagalkan dengan injeksi SQL. Ada yang lain cara untuk menghadapinya:
rencana pengoptimalisasi menggunakan pemindaian indeks daripada pencarian indeks, kemungkinan kebutuhan untuk ekspresi atau fungsi untuk keluar dari wildcard (indeks kemungkinan pada ekspresi atau fungsi)
menggunakan nilai literal sebagai pengganti variabel pengikat berdampak skalabilitas
Kesimpulan
Saya suka pendekatan Joel Spolsky. Itu pintar. Dan itu berhasil.
Tapi begitu saya melihatnya, saya langsung melihat potensi masalah dengan itu, dan bukan sifat saya untuk membiarkannya meluncur. Saya tidak bermaksud kritis terhadap upaya orang lain. Saya tahu banyak pengembang mengambil pekerjaan mereka dengan sangat pribadi, karena mereka berinvestasi begitu banyak ke dalamnya dan mereka sangat peduli tentang itu. Jadi tolong mengerti, ini bukan serangan pribadi. Apa yang saya identifikasi di sini adalah jenis masalah yang muncul dalam produksi daripada pengujian.
Ya, saya sudah jauh dari pertanyaan awal. Tetapi di mana lagi harus meninggalkan catatan tentang apa yang saya anggap sebagai masalah penting dengan jawaban "terpilih" untuk sebuah pertanyaan?