Die ursprüngliche Frage lautete "Wie parametriere ich eine Abfrage ..."
Lassen Sie mich gleich hier feststellen, dass dies keine Antwort ist auf die ursprüngliche Frage ist. Es gibt bereits einige Demonstrationen davon in anderen guten Antworten.
Wenn dies gesagt ist, markieren Sie diese Antwort, stimmen Sie sie ab, markieren Sie sie als keine Antwort ... tun Sie, was Sie für richtig halten.
In der Antwort von Mark Brackett finden Sie die bevorzugte Antwort, die ich (und 231 andere) positiv bewertet habe. Der in seiner Antwort angegebene Ansatz ermöglicht 1) die effektive Verwendung von Bindungsvariablen und 2) die Verwendung von Prädikaten, die sarkierbar sind.
Ausgewählte Antwort
Was ich hier ansprechen möchte, ist der Ansatz in Joel Spolskys Antwort, die Antwort "ausgewählt" als die richtige Antwort.
Joel Spolskys Ansatz ist klug. Und es funktioniert vernünftig, es wird vorhersehbares Verhalten und vorhersehbare Leistung zeigen, wenn "normale" Werte gegeben sind und mit den normativen Randfällen wie NULL und der leeren Zeichenfolge. Und es kann für eine bestimmte Anwendung ausreichend sein.
Um diesen Ansatz zu verallgemeinern, betrachten wir auch die dunkeleren Eckfälle, z. B. wenn die Name
Spalte ein Platzhalterzeichen enthält (wie vom LIKE-Prädikat erkannt). Das am häufigsten verwendete Platzhalterzeichen ist %
(ein Prozentzeichen). Lassen Sie uns jetzt hier damit umgehen und später mit anderen Fällen fortfahren.
Einige Probleme mit% Zeichen
Betrachten Sie einen Namenswert von 'pe%ter'
. (Für die Beispiele hier verwende ich anstelle des Spaltennamens einen Literalzeichenfolgenwert.) Eine Zeile mit dem Namenswert "pe% ter" wird von einer Abfrage des Formulars zurückgegeben:
select ...
where '|peanut|butter|' like '%|' + 'pe%ter' + '|%'
Aber die gleiche Reihe wird nicht nicht zurückgegeben, wenn die Reihenfolge der Suchbegriffe umgekehrt wird:
select ...
where '|butter|peanut|' like '%|' + 'pe%ter' + '|%'
Das Verhalten, das wir beobachten, ist seltsam. Durch Ändern der Reihenfolge der Suchbegriffe in der Liste wird die Ergebnismenge geändert.
Es ist fast selbstverständlich, dass wir nicht wollen pe%ter
mit Erdnussbutter mithalten wollen, egal wie sehr er es mag.
Dunkler Eckfall
(Ja, ich werde zustimmen, dass dies ein dunkler Fall ist. Wahrscheinlich einer, der wahrscheinlich nicht getestet wird. Wir würden keinen Platzhalter in einem Spaltenwert erwarten. Wir können davon ausgehen, dass die Anwendung die Speicherung eines solchen Werts verhindert Nach meiner Erfahrung habe ich selten eine Datenbankbeschränkung gesehen, die Zeichen oder Muster, die als Platzhalter auf der rechten Seite von a gelten, ausdrücklich nicht zulässt LIKE
Vergleichsoperators .
Ein Loch flicken
Ein Ansatz zum Patchen dieses Lochs besteht darin, dem %
Platzhalterzeichen zu entkommen . (Für alle, die mit der Escape-Klausel des Operators nicht vertraut sind, finden Sie hier einen Link zur SQL Server-Dokumentation .
select ...
where '|peanut|butter|'
like '%|' + 'pe\%ter' + '|%' escape '\'
Jetzt können wir das Literal% abgleichen. Wenn wir einen Spaltennamen haben, müssen wir natürlich den Platzhalter dynamisch umgehen. Wir können die REPLACE
Funktion verwenden, um Vorkommen des %
Zeichens zu finden und vor jedem ein Backslash-Zeichen einzufügen, wie folgt:
select ...
where '|pe%ter|'
like '%|' + REPLACE( 'pe%ter' ,'%','\%') + '|%' escape '\'
Damit ist das Problem mit dem Platzhalter% gelöst. Fast.
Entkomme der Flucht
Wir erkennen an, dass unsere Lösung ein weiteres Problem eingeführt hat. Der Fluchtcharakter. Wir sehen, dass wir auch allen Vorkommen des Fluchtcharakters selbst entkommen müssen. Dieses Mal verwenden wir die! als Fluchtcharakter:
select ...
where '|pe%t!r|'
like '%|' + REPLACE(REPLACE( 'pe%t!r' ,'!','!!'),'%','!%') + '|%' escape '!'
Der Unterstrich auch
Jetzt, da wir auf einer Rolle sind, können wir REPLACE
dem Unterstrich-Platzhalter ein weiteres Handle hinzufügen . Und nur zum Spaß verwenden wir diesmal $ als Fluchtzeichen.
select ...
where '|p_%t!r|'
like '%|' + REPLACE(REPLACE(REPLACE( 'p_%t!r' ,'$','$$'),'%','$%'),'_','$_') + '|%' escape '$'
Ich ziehe diesen Ansatz dem Escape vor, da er sowohl in Oracle und MySQL als auch in SQL Server funktioniert. (Normalerweise verwende ich den \ backslash als Escape-Zeichen, da dies das Zeichen ist, das wir in regulären Ausdrücken verwenden. Aber warum durch Konventionen eingeschränkt werden?
Diese nervigen Klammern
Mit SQL Server können Platzhalterzeichen auch als Literale behandelt werden, indem sie in Klammern gesetzt werden []
. Wir sind also noch nicht mit dem Reparieren fertig, zumindest für SQL Server. Da Klammerpaare eine besondere Bedeutung haben, müssen wir uns auch diesen entziehen. Wenn es uns gelingt, den Klammern richtig zu entkommen, müssen wir uns zumindest nicht um den Bindestrich -
und das Karat ^
in den Klammern kümmern. Und wir können jeden verlassen %
und_
Zeichen in den Klammern entkommen, da wir im Grunde die besondere Bedeutung der Klammern deaktiviert haben.
Es sollte nicht so schwer sein, passende Klammerpaare zu finden. Es ist etwas schwieriger als das Auftreten von Singleton% und _ zu behandeln. (Beachten Sie, dass es nicht ausreicht, nur alle Vorkommen von Klammern zu maskieren, da eine einzelne Klammer als Literal betrachtet wird und nicht maskiert werden muss. Die Logik wird etwas unschärfer, als ich verarbeiten kann, ohne mehr Testfälle auszuführen .)
Inline-Ausdruck wird chaotisch
Dieser Inline-Ausdruck in SQL wird länger und hässlicher. Wir können es wahrscheinlich zum Laufen bringen, aber der Himmel hilft der armen Seele, die zurückkommt und es entziffern muss. Als Fan von Inline-Ausdrücken neige ich dazu, hier keinen zu verwenden, hauptsächlich, weil ich keinen Kommentar hinterlassen möchte, der den Grund für das Durcheinander erklärt und mich dafür entschuldigt.
Eine Funktion wo?
Okay, wenn wir das nicht als Inline-Ausdruck in SQL behandeln, ist die nächstgelegene Alternative eine benutzerdefinierte Funktion. Und wir wissen, dass dies die Dinge nicht beschleunigen wird (es sei denn, wir können einen Index dafür definieren, wie wir es mit Oracle könnten). Wenn wir eine Funktion erstellen müssen, tun wir dies möglicherweise besser in dem Code, der SQL aufruft Aussage.
Und diese Funktion kann je nach DBMS und Version einige Verhaltensunterschiede aufweisen. (Ein Dankeschön an alle Java-Entwickler, die daran interessiert sind, jedes Datenbankmodul austauschbar zu verwenden.)
Fachwissen
Möglicherweise verfügen wir über spezielle Kenntnisse der Domäne für die Spalte (dh der Menge der zulässigen Werte, die für die Spalte erzwungen werden. Möglicherweise wissen wir a priori, dass die in der Spalte gespeicherten Werte niemals ein Prozentzeichen, einen Unterstrich oder eine Klammer enthalten In diesem Fall fügen wir nur einen kurzen Kommentar hinzu, dass diese Fälle abgedeckt sind.
Die in der Spalte gespeicherten Werte können% oder _ Zeichen zulassen, aber eine Einschränkung kann erfordern, dass diese Werte maskiert werden, möglicherweise unter Verwendung eines definierten Zeichens, so dass die Werte wie der Vergleich "sicher" sind. Nochmals ein kurzer Kommentar zu den zulässigen Werten und insbesondere zu dem Zeichen, das als Escape-Zeichen verwendet wird, und zu Joel Spolskys Ansatz.
Ohne das Fachwissen und eine Garantie ist es für uns jedoch wichtig, zumindest die Behandlung dieser obskuren Eckfälle in Betracht zu ziehen und zu prüfen, ob das Verhalten angemessen und "gemäß der Spezifikation" ist.
Andere Themen zusammengefasst
Ich glaube, andere haben bereits ausreichend auf einige der anderen häufig betrachteten Problembereiche hingewiesen:
SQL-Injection (unter Verwendung von scheinbar vom Benutzer bereitgestellten Informationen und Aufnahme dieser Informationen in den SQL-Text, anstatt sie über Bindevariablen bereitzustellen. Die Verwendung von Bindevariablen ist nicht erforderlich, sondern nur ein praktischer Ansatz, um die SQL-Injection zu verhindern. Es gibt andere Möglichkeiten, damit umzugehen:
Optimierungsplan mit Index-Scan anstelle von Index-Suchvorgängen, mögliche Notwendigkeit eines Ausdrucks oder einer Funktion zum Escape-Platzieren von Platzhaltern (möglicher Index für Ausdruck oder Funktion)
Die Verwendung von Literalwerten anstelle von Bindungsvariablen wirkt sich auf die Skalierbarkeit aus
Fazit
Ich mag Joel Spolskys Ansatz. Es ist klug. Und es funktioniert.
Aber sobald ich es sah, sah ich sofort ein potenzielles Problem damit, und es liegt nicht in meiner Natur, es gleiten zu lassen. Ich will nicht kritisch gegenüber den Bemühungen anderer sein. Ich weiß, dass viele Entwickler ihre Arbeit sehr persönlich nehmen, weil sie so viel in sie investieren und sich so sehr darum kümmern. Bitte haben Sie Verständnis dafür, dass dies kein persönlicher Angriff ist. Was ich hier identifiziere, ist die Art von Problem, das eher in der Produktion als beim Testen auftritt.
Ja, ich bin weit von der ursprünglichen Frage entfernt. Aber wo sonst sollte ich diesen Hinweis zu dem hinterlassen, was ich für ein wichtiges Problem mit der "ausgewählten" Antwort auf eine Frage halte?