Pierwotne pytanie brzmiało: „Jak sparametryzować zapytanie ...”
Pozwolę sobie stwierdzić, że nie jest to odpowiedź na pierwotne pytanie. Istnieją już pewne tego dowody w innych dobrych odpowiedziach.
Mając to na uwadze, śmiało zaznaczaj tę odpowiedź, głosuj na nią, oznacz ją jako brak odpowiedzi ... rób wszystko, co uważasz za słuszne.
Zobacz odpowiedź Marka Bracketta na preferowaną odpowiedź, którą głosowałem (i 231 innych osób). Podane w jego odpowiedzi podejście pozwala 1) na efektywne wykorzystanie zmiennych wiązania i 2) na predykaty, które są podatne na kalkulację.
Wybrana odpowiedź
Chciałbym się tutaj zająć podejściem podanym w odpowiedzi Joela Spolsky'ego, odpowiedź „wybrana” jako właściwa odpowiedź.
Podejście Joela Spolsky'ego jest sprytne. I działa rozsądnie, będzie wykazywać przewidywalne zachowanie i przewidywalną wydajność, biorąc pod uwagę „normalne” wartości, a także z normatywnymi przypadkami krawędzi, takimi jak NULL i pusty ciąg. I może być wystarczające dla konkretnego zastosowania.
Ale jeśli chodzi o uogólnienie tego podejścia, rozważmy również bardziej niejasne przypadki narożników, na przykład gdy Name
kolumna zawiera znak wieloznaczny (rozpoznawany przez predykat LIKE). Najczęściej stosowanym znakiem wieloznacznym jest%
znakiem (znak procentu.). Zajmijmy się tym teraz, a później przejdźmy do innych spraw.
Niektóre problemy ze znakiem%
Rozważ wartość nazwy 'pe%ter'
. (W podanych tu przykładach zamiast nazwy kolumny używam literału). Wiersz o wartości nazwy `` pe% ter '' zostanie zwrócony przez zapytanie o formę:
select ...
where '|peanut|butter|' like '%|' + 'pe%ter' + '|%'
Ale ten sam wiersz nie zostanie zwrócony, jeśli kolejność wyszukiwanych haseł zostanie odwrócona:
select ...
where '|butter|peanut|' like '%|' + 'pe%ter' + '|%'
Zachowanie, które obserwujemy, jest dość dziwne. Zmiana kolejności wyszukiwanych haseł na liście zmienia zestaw wyników.
To prawie oczywiste, że możemy nie chcieć pe%ter
dopasowywać masła orzechowego, bez względu na to, jak bardzo to lubi.
Niewyraźna obudowa narożna
(Tak, zgodzę się, że jest to niejasny przypadek. Prawdopodobnie taki, który prawdopodobnie nie zostanie przetestowany. Nie spodziewalibyśmy się znaku wieloznacznego w wartości kolumny. Możemy założyć, że aplikacja zapobiega przechowywaniu takiej wartości. Ale z mojego doświadczenia rzadko widziałem ograniczenie bazy danych, które wyraźnie zabraniało znaków lub wzorców, które byłyby uważane za symbole wieloznaczne po prawej stronie LIKE
operatora porównania.
Łatanie dziury
Jednym ze sposobów na załatanie tej dziury jest uniknięcie %
znaku wieloznacznego. (Dla każdego, kto nie zna klauzuli Escape na operatorze, oto link do dokumentacji SQL Server .
select ...
where '|peanut|butter|'
like '%|' + 'pe\%ter' + '|%' escape '\'
Teraz możemy dopasować dosłowny%. Oczywiście, kiedy będziemy mieli nazwę kolumny, będziemy musieli dynamicznie uciec od znaku wieloznacznego. Możemy użyć tej REPLACE
funkcji, aby znaleźć wystąpienia %
znaku i wstawić przed każdym odwrotnym ukośnikiem:
select ...
where '|pe%ter|'
like '%|' + REPLACE( 'pe%ter' ,'%','\%') + '|%' escape '\'
To rozwiązuje problem z% wildcard. Prawie.
Uciec ucieczki
Zdajemy sobie sprawę, że nasze rozwiązanie wprowadziło kolejny problem. Postać ucieczki. Widzimy, że będziemy również musieli uciec przed wszelkimi przypadkami samej postaci ucieczki. Tym razem korzystamy z! jako postać ucieczki:
select ...
where '|pe%t!r|'
like '%|' + REPLACE(REPLACE( 'pe%t!r' ,'!','!!'),'%','!%') + '|%' escape '!'
Podkreślenie też
Teraz, gdy jesteśmy na rzucie, możemy dodać kolejny REPLACE
uchwyt znaku wieloznacznego podkreślenia. I dla zabawy, tym razem użyjemy $ jako postaci ucieczki.
select ...
where '|p_%t!r|'
like '%|' + REPLACE(REPLACE(REPLACE( 'p_%t!r' ,'$','$$'),'%','$%'),'_','$_') + '|%' escape '$'
Wolę to podejście od ucieczki, ponieważ działa w Oracle i MySQL oraz SQL Server. (Zazwyczaj używam ukośnika odwrotnego \ jako znaku ucieczki, ponieważ jest to znak, którego używamy w wyrażeniach regularnych. Ale po co ograniczać się konwencją!
Te nieznośne nawiasy
SQL Server pozwala również traktować znaki wieloznaczne jako literały, umieszczając je w nawiasach []
. Więc nie skończyliśmy jeszcze naprawiania, przynajmniej dla SQL Server. Ponieważ pary nawiasów mają specjalne znaczenie, musimy również uciec przed nimi. Jeśli uda nam się odpowiednio uciec od nawiasów, to przynajmniej nie będziemy musieli zawracać sobie głowy łącznikiem -
i karatem ^
w nawiasach. I możemy zostawić każdego %
i_
znaków wewnątrz nawiasów uciekł, ponieważ będziemy mieli w zasadzie wyłączone specjalnego znaczenia nawiasów.
Znalezienie pasujących par nawiasów nie powinno być takie trudne. Jest to trochę trudniejsze niż radzenie sobie z wystąpieniem singletonu% i _. (Zauważ, że nie wystarczy po prostu uciec od wszystkich wystąpień nawiasów, ponieważ nawias singletonowy jest uważany za dosłowny i nie trzeba go uciekać. Logika staje się nieco bardziej niewyraźna niż mogę obsłużyć więcej testów .)
Wyrażenie wbudowane staje się nieporządne
To wyrażenie wbudowane w SQL staje się dłuższe i brzydsze. Prawdopodobnie możemy sprawić, by działało, ale niebo pomoże biednej duszy, która za nią stoi i musi ją rozszyfrować. Ponieważ jestem fanem wyrażeń wbudowanych, nie jestem skłonny do używania go tutaj, głównie dlatego, że nie chcę zostawiać komentarza wyjaśniającego przyczynę bałaganu i przepraszającego za to.
Funkcja gdzie?
Ok, więc jeśli nie będziemy traktować tego jako wyrażenia wbudowanego w SQL, najbliższą alternatywą, jaką mamy, jest funkcja zdefiniowana przez użytkownika. I wiemy, że to niczego nie przyspieszy (chyba że możemy zdefiniować na nim indeks, tak jak moglibyśmy to zrobić z Oracle.) Jeśli musimy stworzyć funkcję, lepiej to zrobić w kodzie wywołującym SQL komunikat.
Ta funkcja może mieć pewne różnice w zachowaniu, zależne od DBMS i wersji. (Pozdrawiam wszystkich programistów Java, którzy tak chętnie korzystają z dowolnego silnika bazy danych zamiennie).
Znajomość domen
Możemy mieć specjalistyczną wiedzę na temat domeny dla kolumny (tj. Zestawu dopuszczalnych wartości wymuszonych dla kolumny. Możemy z góry wiedzieć, że wartości przechowywane w kolumnie nigdy nie będą zawierały znaku procentu, podkreślenia lub nawiasu pary. W takim przypadku zamieszczamy tylko krótki komentarz, że te przypadki są objęte gwarancją.
Wartości przechowywane w kolumnie mogą zezwalać na znaki% lub _, ale ograniczenie może wymagać zmiany tych wartości, być może przy użyciu określonego znaku, tak aby wartości LIKE były „bezpieczne”. Ponownie, szybki komentarz na temat dozwolonego zestawu wartości, a zwłaszcza tego, która postać jest używana jako postać ucieczki, i podejdź do podejścia Joela Spolsky'ego.
Ale bez specjalistycznej wiedzy i gwarancji ważne jest, abyśmy przynajmniej rozważyli obsługę tych niejasnych narożnych spraw i zastanowili się, czy zachowanie jest rozsądne i „zgodne ze specyfikacją”.
Inne problemy podsumowano
Uważam, że inni już w wystarczającym stopniu wskazali niektóre z innych powszechnie rozważanych obszarów zainteresowania:
Wstrzykiwanie SQL (przyjmowanie informacji, które wydają się być informacjami dostarczonymi przez użytkownika, i uwzględnianie ich w tekście SQL zamiast dostarczania ich przez zmienne powiązania. Używanie zmiennych powiązania nie jest wymagane, jest to tylko jedno wygodne podejście do udaremnienia za pomocą wstrzykiwania SQL. Istnieją inne sposoby radzenia sobie z tym:
plan optymalizacyjny wykorzystujący skanowanie indeksów zamiast przeszukiwania indeksów, możliwa potrzeba wyrażenia lub funkcji do ucieczki symboli wieloznacznych (możliwy indeks wyrażenia lub funkcji)
użycie wartości literalnych zamiast zmiennych powiązań wpływa na skalowalność
Wniosek
Lubię podejście Joela Spolsky'ego. To sprytne. I to działa.
Ale gdy tylko to zobaczyłem, natychmiast zobaczyłem potencjalny problem z tym i nie jest moją naturą pozwolić mu się ześlizgnąć. Nie chcę krytykować wysiłków innych. Wiem, że wielu programistów traktuje swoją pracę bardzo osobiście, ponieważ tyle w nią inwestują i tak bardzo się o nią troszczą. Proszę więc zrozumieć, że to nie jest osobisty atak. To, co tu identyfikuję, to rodzaj problemu, który pojawia się w produkcji, a nie podczas testowania.
Tak, odszedłem daleko od pierwotnego pytania. Ale gdzie jeszcze zostawić tę notatkę dotyczącą tego, co uważam za ważny problem z „wybraną” odpowiedzią na pytanie?