Orijinal soru "Bir sorguyu nasıl parametrelendiririm ..." idi.
Burada belirteyim, bunun asıl sorunun cevabı değil . Diğer iyi cevaplarda zaten bazı gösteriler var.
Bununla birlikte, devam edin ve bu cevabı işaretleyin, aşağı indirin, bir cevap değil olarak işaretleyin ... doğru olduğuna inandığınız her şeyi yapın.
Ben (ve diğer 231 kişi) tarafından iptal edilen tercih edilen cevap için Mark Brackett'in cevabına bakınız. Cevabında verilen yaklaşım, 1) bağlama değişkenlerinin etkili kullanımına ve 2) anlaşılabilir tahminlere izin verir.
Seçilen cevap
Burada ele almak istediğim, Joel Spolsky'nin cevabında verilen yaklaşım, doğru cevap "seçilmiş" cevabı.
Joel Spolsky'nin yaklaşımı zekidir. Ve makul bir şekilde çalışır, "normal" değerler verildiğinde ve NULL ve boş dize gibi normatif kenar durumlarıyla öngörülebilir davranış ve öngörülebilir performans sergileyecektir. Ve belirli bir uygulama için yeterli olabilir.
Ancak bu yaklaşımı genelleştirmek açısından, Name
sütunun bir joker karakter içerdiği (LIKE yüklemi tarafından tanındığı gibi) gibi daha belirsiz köşe vakalarını da ele alalım . En sık kullandığım joker karakter %
(yüzde işareti). Şimdi burada bununla başa çıkalım ve daha sonra diğer davalara geçelim.
% Karakteriyle ilgili bazı sorunlar
Ad değerini düşünün 'pe%ter'
. (Buradaki örnekler için, sütun adının yerine değişmez bir dize değeri kullanıyorum.) Ad değeri `` pe% ter '' olan bir satır, formun bir sorgusu tarafından döndürülür:
select ...
where '|peanut|butter|' like '%|' + 'pe%ter' + '|%'
Ama bu aynı satır olacak değil arama terimlerinin sırası tersine eğer iade edilmesi:
select ...
where '|butter|peanut|' like '%|' + 'pe%ter' + '|%'
Gözlemlediğimiz davranış biraz tuhaf. Listedeki arama terimlerinin sırasını değiştirmek sonuç kümesini değiştirir.
Neredeyse pe%ter
ne olursa olsun, fıstık ezmesini eşleştirmek istemeyeceğimizi söylemeye gerek yok .
Gizli köşe kılıfı
(Evet, bunun belirsiz bir durum olduğunu kabul edeceğim. Muhtemelen test edilmesi muhtemel olmayan bir durum. Sütun değerinde bir joker karakter beklemezdik. Uygulamanın böyle bir değerin depolanmasını önlediğini varsayabiliriz. Deneyimlerime göre, nadiren bir LIKE
karşılaştırma operatörünün sağ tarafında joker karakterler olarak kabul edilecek karakterlere veya desenlere izin verilmeyen bir veritabanı kısıtlaması gördüm .
Delik açma
Bu deliği yamalamak için bir yaklaşım %
joker karakterden kaçmaktır . (İşlecdeki çıkış yan tümcesine aşina olmayan herkes için, burada SQL Server belgelerine bir bağlantı verilmiştir .
select ...
where '|peanut|butter|'
like '%|' + 'pe\%ter' + '|%' escape '\'
Şimdi% değişmezi ile eşleşebiliriz. Tabii ki, bir sütun ismimiz olduğunda, joker karakterden dinamik olarak kaçmamız gerekecek. Bu REPLACE
işlevi %
karakterin oluşumlarını bulmak ve her birinin önüne bir ters eğik çizgi karakteri eklemek için kullanabiliriz:
select ...
where '|pe%ter|'
like '%|' + REPLACE( 'pe%ter' ,'%','\%') + '|%' escape '\'
Bu,% joker karakteriyle sorunu çözer. Neredeyse.
Kaçış kaçış
Çözümümüzün başka bir sorun yarattığını kabul ediyoruz. Kaçış karakteri. Ayrıca kaçış karakterinin kendisinden de kaçmamız gerektiğini görüyoruz. Bu sefer! kaçış karakteri olarak:
select ...
where '|pe%t!r|'
like '%|' + REPLACE(REPLACE( 'pe%t!r' ,'!','!!'),'%','!%') + '|%' escape '!'
Alt çizgi de
Artık bir rulodayız, REPLACE
alt çizgi joker karakterini başka bir tutamaç ekleyebiliriz . Ve sadece eğlence için, bu sefer $ kaçış karakteri olarak kullanacağız.
select ...
where '|p_%t!r|'
like '%|' + REPLACE(REPLACE(REPLACE( 'p_%t!r' ,'$','$$'),'%','$%'),'_','$_') + '|%' escape '$'
Oracle ve MySQL'in yanı sıra SQL Server'da da çalıştığı için bu yaklaşımdan kaçmayı tercih ederim. (Genellikle \ backslash karakterini kaçış karakteri olarak kullanıyorum, çünkü bu normal ifadelerde kullandığımız karakter. Ama neden konvansiyonla kısıtlanmalı!
Bu sinir bozucu parantez
SQL Server, joker karakterlerin köşeli parantez içine alınmasıyla değişmez değerler olarak ele alınmasını da sağlar []
. Bu yüzden henüz en azından SQL Server için düzeltme yapmadık. Parantez çiftleri özel bir anlama sahip olduğundan, bunlardan da kaçmamız gerekecek. Parantezlerden düzgün bir şekilde kaçmayı başarabilirsek, en azından parantez içindeki tire -
ve karat ile uğraşmak zorunda kalmayacağız ^
. Ve biz herhangi bırakabilir %
ve _
parantez kaçan içine temelde parantez özel bir anlam devre dışı bırakmış olacağı için, karakterleri.
Eşleşen parantez çiftlerini bulmak o kadar da zor olmamalı. Singleton% ve _ oluşumlarını işlemekten biraz daha zor. (Parantezlerin tüm örneklerinden kaçmanın yeterli olmadığına dikkat edin, çünkü tek bir parantez bir değişmez olarak kabul edilir ve kaçması gerekmez. .)
Satır içi ifade dağınık hale geliyor
SQL'deki bu satır içi ifade daha uzun ve çirkinleşiyor. Muhtemelen işe yarayabiliriz, ama cennet geride kalan ve deşifre etmek zorunda olan fakir ruha yardım eder. Satır içi ifadeler için bir hayranım kadar, burada bir tane kullanmamaya meyilliyim, çünkü karışıklığın nedenini açıklayan ve bunun için özür dileyerek bir yorum bırakmak istemiyorum.
Nerede bir fonksiyon?
Tamam, bu yüzden, bunu SQL'de satır içi bir ifade olarak ele almazsak, sahip olduğumuz en yakın alternatif kullanıcı tanımlı bir işlevdir. Ve bunun hiçbir şeyi hızlandırmayacağını biliyoruz (Oracle ile yapabileceğimiz gibi bir dizin tanımlayamazsak.) Bir işlev oluşturmanız gerekirse, SQL'i çağıran kodda bunu daha iyi yapabiliriz Beyan.
Ve bu işlevin DBMS ve sürüme bağlı olarak bazı davranış farklılıkları olabilir. (Herhangi bir veritabanı motorunu birbirinin yerine kullanabilmeye hevesli olan tüm Java geliştiricilerine seslenin.)
Alan bilgisi
Biz olduğunu, sütun için uygulanan izin verilen değerlerin kümesi. Biz biliyor olabilir (sütun için etki alanının uzman bilgiye sahip olabilir önsel sütunda depolanan değerler yüzde işareti, bir alt çizgi ya da destek ihtiva asla Bu durumda, bu davaların ele alındığına dair hızlı bir yorum ekliyoruz.
Sütunda depolanan değerler% veya _ karaktere izin verebilir, ancak bir sınırlama, değerlerin LIKE karşılaştırması "güvenli" olacak şekilde, tanımlanmış bir karakter kullanarak bu değerlerin kaçmasını gerektirebilir. Yine, izin verilen değerler kümesi ve özellikle hangi karakterin kaçış karakteri olarak kullanıldığı ve Joel Spolsky'nin yaklaşımıyla ilgili hızlı bir yorum.
Ancak, uzmanlık bilgisi ve bir garanti yoksa, en azından bu belirsiz köşe davalarını ele almayı ve davranışın makul ve "şartname uyarınca" olup olmadığını düşünmemiz önemlidir.
Yeniden özetlenen diğer konular
Başkalarının zaten yaygın olarak düşünülen diğer endişe alanlarından bazılarını yeterince işaret ettiklerine inanıyorum:
SQL enjeksiyonu (kullanıcı tarafından sağlanan bilgiler gibi görünen ve bind değişkenleri aracılığıyla bunları sağlamak yerine SQL metnine dahil edilenler. Bind değişkenlerini kullanmak gerekli değildir, SQL enjeksiyonu ile mücadele etmek için sadece uygun bir yaklaşımdır. onunla başa çıkmanın yolları:
dizin arama yerine dizin taraması kullanan optimize edici planı, joker karakterlerden kaçmak için bir ifade veya işlev ihtiyacı (ifade veya işlev üzerindeki olası dizin)
bağlama değişkenleri yerine değişmez değerlerin kullanılması ölçeklenebilirliği etkiler
Sonuç
Joel Spolsky'nin yaklaşımını seviyorum. Akıllı. Ve çalışıyor.
Ama onu görür görmez, hemen onunla ilgili potansiyel bir sorun gördüm ve kaymasına izin vermek benim doğam değil. Başkalarının çabalarını eleştirmek istemiyorum. Birçok geliştiricinin işlerini çok kişisel aldığını biliyorum, çünkü ona çok yatırım yapıyorlar ve çok önemsiyorlar. Lütfen anlayın, bu kişisel bir saldırı değil. Burada belirlediğim, testten ziyade üretimde ortaya çıkan sorun türüdür.
Evet, asıl sorudan uzaklaştım. Ama bir soru için "seçilmiş" cevap ile önemli bir konu olarak düşündüğüm konu hakkında bu notu başka nerede bırakabilirim?