La question d'origine était "Comment paramétrer une requête ..."
Permettez-moi de dire ici que ce n'est pas une réponse à la question initiale. Il y a déjà quelques démonstrations de cela dans d'autres bonnes réponses.
Cela dit, allez-y et marquez cette réponse, votez-la, marquez-la comme n'étant pas une réponse ... faites tout ce que vous pensez être juste.
Voir la réponse de Mark Brackett pour la réponse préférée que j'ai (et 231 autres) votée. L'approche donnée dans sa réponse permet 1) une utilisation efficace des variables de liaison et 2) des prédicats qui sont sargables.
Réponse sélectionnée
Ce que je veux aborder ici, c'est l'approche donnée dans la réponse de Joel Spolsky, la réponse «sélectionnée» comme bonne réponse.
L'approche de Joel Spolsky est intelligente. Et cela fonctionne raisonnablement, il va présenter un comportement prévisible et des performances prévisibles, étant donné les valeurs "normales", et avec les cas de bord normatifs, tels que NULL et la chaîne vide. Et cela peut être suffisant pour une application particulière.
Mais en termes de généralisation de cette approche, considérons également les cas d'angle les plus obscurs, comme lorsque la Name
colonne contient un caractère générique (tel que reconnu par le prédicat LIKE.) Le caractère générique que je vois le plus couramment utilisé est %
(un signe de pourcentage). Traitons donc cela ici maintenant, et passons plus tard à d'autres cas.
Quelques problèmes avec% character
Considérez une valeur Nom de 'pe%ter'
. (Pour les exemples ici, j'utilise une valeur de chaîne littérale à la place du nom de la colonne.) Une ligne avec une valeur Name de `` pe% ter 'serait retournée par une requête de la forme:
select ...
where '|peanut|butter|' like '%|' + 'pe%ter' + '|%'
Mais cette même ligne ne sera pas retournée si l'ordre des termes de recherche est inversé:
select ...
where '|butter|peanut|' like '%|' + 'pe%ter' + '|%'
Le comportement que nous observons est assez étrange. La modification de l'ordre des termes de recherche dans la liste modifie l'ensemble de résultats.
Il va presque sans dire que nous pourrions ne pas vouloir pe%ter
faire correspondre le beurre d'arachide, peu importe combien il l'aime.
Boîtier d'angle obscur
(Oui, je conviendrai que c'est un cas obscur. Probablement un qui n'est pas susceptible d'être testé. Nous ne nous attendrions pas à un caractère générique dans une valeur de colonne. Nous pouvons supposer que l'application empêche une telle valeur d'être stockée. Mais d'après mon expérience, j'ai rarement vu une contrainte de base de données qui interdisait spécifiquement les caractères ou les modèles qui seraient considérés comme des caractères génériques sur le côté droit d'un LIKE
opérateur de comparaison.
Patcher un trou
Une approche pour corriger ce trou consiste à échapper au %
caractère générique. (Pour ceux qui ne connaissent pas la clause d'échappement de l'opérateur, voici un lien vers la documentation de SQL Server .
select ...
where '|peanut|butter|'
like '%|' + 'pe\%ter' + '|%' escape '\'
Maintenant, nous pouvons faire correspondre le% littéral. Bien sûr, lorsque nous avons un nom de colonne, nous allons devoir échapper dynamiquement au caractère générique. Nous pouvons utiliser la REPLACE
fonction pour trouver des occurrences du %
caractère et insérer un caractère barre oblique inverse devant chacun, comme ceci:
select ...
where '|pe%ter|'
like '%|' + REPLACE( 'pe%ter' ,'%','\%') + '|%' escape '\'
Cela résout donc le problème avec le caractère générique%. Presque.
Échapper à l'évasion
Nous reconnaissons que notre solution a introduit un autre problème. Le personnage d'évasion. Nous voyons que nous allons également avoir besoin d'échapper à toute occurrence de caractère d'échappement lui-même. Cette fois, nous utilisons le! comme personnage d'échappement:
select ...
where '|pe%t!r|'
like '%|' + REPLACE(REPLACE( 'pe%t!r' ,'!','!!'),'%','!%') + '|%' escape '!'
Le soulignement aussi
Maintenant que nous sommes sur une lancée, nous pouvons ajouter une autre REPLACE
poignée le caractère générique de soulignement. Et juste pour le plaisir, cette fois, nous utiliserons $ comme caractère d'échappement.
select ...
where '|p_%t!r|'
like '%|' + REPLACE(REPLACE(REPLACE( 'p_%t!r' ,'$','$$'),'%','$%'),'_','$_') + '|%' escape '$'
Je préfère cette approche à l'évasion car elle fonctionne dans Oracle et MySQL ainsi que SQL Server. (J'utilise habituellement la \ backslash comme caractère d'échappement, puisque c'est le caractère que nous utilisons dans les expressions régulières. Mais pourquoi être contraint par convention!
Ces supports embêtants
SQL Server permet également de traiter les caractères génériques comme des littéraux en les mettant entre crochets []
. Nous n'avons donc pas encore terminé la correction, du moins pour SQL Server. Étant donné que les paires de crochets ont une signification particulière, nous devrons également y échapper. Si nous parvenons à échapper correctement les crochets, alors au moins nous n'aurons pas à nous soucier du trait d'union -
et du carat ^
entre les crochets. Et nous pouvons laisser tout caractère %
et _
à l'intérieur des crochets échappés, car nous aurons essentiellement désactivé la signification spéciale des crochets.
Trouver des paires de supports correspondants ne devrait pas être si difficile. C'est un peu plus difficile que de gérer les occurrences de singleton% et _. (Notez qu'il ne suffit pas d'échapper à toutes les occurrences de crochets, car un crochet singleton est considéré comme un littéral et n'a pas besoin d'être échappé. La logique devient un peu plus floue que je ne peux gérer sans exécuter plus de cas de test .)
L'expression en ligne devient désordonnée
Cette expression en ligne dans le SQL devient plus longue et plus laide. Nous pouvons probablement le faire fonctionner, mais le ciel aide la pauvre âme qui vient derrière et doit la déchiffrer. Comme je suis un grand fan des expressions en ligne, je suis enclin à ne pas en utiliser ici, principalement parce que je ne veux pas avoir à laisser un commentaire expliquant la raison du désordre et m'en excusant.
Une fonction où?
D'accord, donc, si nous ne traitons pas cela comme une expression en ligne dans le SQL, l'alternative la plus proche que nous avons est une fonction définie par l'utilisateur. Et nous savons que cela n'accélérera pas les choses (à moins que nous ne puissions définir un index dessus, comme nous le pourrions avec Oracle.) Si nous devons créer une fonction, nous ferions mieux de le faire dans le code qui appelle le SQL. déclaration.
Et cette fonction peut avoir des différences de comportement, selon le SGBD et la version. (Un grand bravo à tous les développeurs Java qui souhaitent pouvoir utiliser n'importe quel moteur de base de données de manière interchangeable.)
Connaissance du domaine
Nous pouvons avoir une connaissance spécialisée du domaine de la colonne (c'est-à-dire l'ensemble des valeurs autorisées appliquées pour la colonne. Nous pouvons savoir a priori que les valeurs stockées dans la colonne ne contiendront jamais de signe de pourcentage, de soulignement ou de parenthèse Dans ce cas, nous incluons simplement un commentaire rapide indiquant que ces cas sont couverts.
Les valeurs stockées dans la colonne peuvent autoriser des caractères% ou _, mais une contrainte peut exiger que ces valeurs soient échappées, peut-être en utilisant un caractère défini, de sorte que les valeurs soient COMME une comparaison "sûre". Encore une fois, un bref commentaire sur l'ensemble de valeurs autorisé, et en particulier sur le caractère utilisé comme caractère d'échappement, et suivez l'approche de Joel Spolsky.
Mais, en l'absence de connaissances spécialisées et d'une garantie, il est important pour nous d'envisager au moins de gérer ces cas de coins obscurs et de déterminer si le comportement est raisonnable et "selon les spécifications".
Autres questions récapitulées
Je crois que d'autres ont déjà suffisamment souligné certains des autres sujets de préoccupation couramment considérés:
Injection SQL (en prenant ce qui semble être des informations fournies par l'utilisateur, et en l'incluant dans le texte SQL plutôt que de les fournir via des variables de liaison. L'utilisation de variables de liaison n'est pas requise, ce n'est qu'une approche pratique pour contrecarrer l'injection SQL. Il existe d'autres comment y faire face:
plan d'optimisation utilisant le balayage d'index plutôt que la recherche d'index, besoin éventuel d'une expression ou d'une fonction pour échapper les caractères génériques (index possible sur l'expression ou la fonction)
l'utilisation de valeurs littérales à la place des variables de liaison affecte l'évolutivité
Conclusion
J'aime l'approche de Joel Spolsky. C'est intelligent. Et il fonctionne.
Mais dès que je l'ai vu, j'ai immédiatement vu un problème potentiel avec lui, et ce n'est pas ma nature de le laisser glisser. Je ne veux pas critiquer les efforts des autres. Je sais que de nombreux développeurs prennent leur travail très personnellement, car ils y investissent tellement et ils se soucient tellement de lui. Comprenez donc, ce n'est pas une attaque personnelle. Ce que j'identifie ici, c'est le type de problème qui survient dans la production plutôt que dans les tests.
Oui, je suis allé loin de la question d'origine. Mais où laisser cette note concernant ce que je considère comme un problème important avec la réponse "sélectionnée" à une question?