La pregunta original era "¿Cómo parametrizo una consulta ..."
Permítanme decir aquí, que esta no es una respuesta a la pregunta original. Ya hay algunas demostraciones de eso en otras buenas respuestas.
Dicho esto, continúe y marque esta respuesta, vote hacia abajo, márquela como no una respuesta ... haga lo que crea que es correcto.
Vea la respuesta de Mark Brackett para la respuesta preferida que yo (y otras 231) voté. El enfoque dado en su respuesta permite 1) el uso efectivo de variables de enlace, y 2) para predicados que son sargable.
Respuesta seleccionada
Lo que quiero abordar aquí es el enfoque dado en la respuesta de Joel Spolsky, la respuesta "seleccionada" como la respuesta correcta.
El enfoque de Joel Spolsky es inteligente. Y funciona razonablemente, exhibirá un comportamiento predecible y un rendimiento predecible, dados los valores "normales", y con los casos límite normativos, como NULL y la cadena vacía. Y puede ser suficiente para una aplicación en particular.
Pero en términos de generalización de este enfoque, consideremos también los casos de esquina más oscuros, como cuando la Name
columna contiene un carácter comodín (como lo reconoce el predicado LIKE). El carácter comodín que veo más comúnmente usado es %
(un signo de porcentaje). Así que lidiemos con eso aquí ahora, y luego pasemos a otros casos.
Algunos problemas con% character
Considere un valor de Nombre de 'pe%ter'
. (Para los ejemplos aquí, utilizo un valor de cadena literal en lugar del nombre de la columna.) Una consulta del formulario devolvería una fila con un valor de Nombre de 'pe% ter':
select ...
where '|peanut|butter|' like '%|' + 'pe%ter' + '|%'
Pero esa misma fila no se devolverá si se invierte el orden de los términos de búsqueda:
select ...
where '|butter|peanut|' like '%|' + 'pe%ter' + '|%'
El comportamiento que observamos es un poco extraño. Cambiar el orden de los términos de búsqueda en la lista cambia el conjunto de resultados.
Casi no hace falta decir que no queremos pe%ter
igualar la mantequilla de maní, no importa cuánto le guste.
Estuche de esquina oscuro
(Sí, estaré de acuerdo en que este es un caso oscuro. Probablemente uno que probablemente no sea probado. No esperaríamos un comodín en el valor de una columna. Podemos suponer que la aplicación evita que se almacene dicho valor. Pero En mi experiencia, rara vez he visto una restricción de la base de datos que específicamente no permitía caracteres o patrones que se considerarían comodines en el lado derecho de un LIKE
operador de comparación.
Parchar un agujero
Un enfoque para parchar este agujero es escapar del %
personaje comodín. (Para cualquiera que no esté familiarizado con la cláusula de escape del operador, aquí hay un enlace a la documentación de SQL Server .
select ...
where '|peanut|butter|'
like '%|' + 'pe\%ter' + '|%' escape '\'
Ahora podemos hacer coincidir el% literal. Por supuesto, cuando tenemos un nombre de columna, vamos a necesitar escapar dinámicamente del comodín. Podemos usar la REPLACE
función para encontrar ocurrencias del %
personaje e insertar un carácter de barra diagonal inversa delante de cada uno, de esta manera:
select ...
where '|pe%ter|'
like '%|' + REPLACE( 'pe%ter' ,'%','\%') + '|%' escape '\'
Eso resuelve el problema con el comodín%. Casi.
Escapar del escape
Reconocemos que nuestra solución ha introducido otro problema. El personaje de escape. Vemos que también vamos a necesitar escapar de cualquier ocurrencia del personaje de escape en sí. Esta vez, usamos el! como el personaje de escape:
select ...
where '|pe%t!r|'
like '%|' + REPLACE(REPLACE( 'pe%t!r' ,'!','!!'),'%','!%') + '|%' escape '!'
El guión bajo también
Ahora que estamos en un rollo, podemos agregar otro REPLACE
identificador al comodín de subrayado. Y solo por diversión, esta vez, usaremos $ como el personaje de escape.
select ...
where '|p_%t!r|'
like '%|' + REPLACE(REPLACE(REPLACE( 'p_%t!r' ,'$','$$'),'%','$%'),'_','$_') + '|%' escape '$'
Prefiero este enfoque a escapar porque funciona en Oracle y MySQL, así como en SQL Server. (Usualmente uso la barra invertida \ como el carácter de escape, ya que ese es el carácter que usamos en las expresiones regulares. ¡Pero por qué estar limitado por la convención!
Esos corchetes molestos
SQL Server también permite que los caracteres comodín se traten como literales encerrándolos entre paréntesis []
. Así que todavía no hemos terminado de arreglar, al menos para SQL Server. Dado que los pares de paréntesis tienen un significado especial, también necesitaremos escapar de esos. Si logramos escapar adecuadamente de los corchetes, al menos no tendremos que molestarnos con el guión -
y el quilate ^
dentro de los corchetes. Y podemos dejar cualquier %
y _
dentro de los corchetes escapados, ya que básicamente habremos deshabilitado el significado especial de los corchetes.
Encontrar pares de paréntesis no debería ser tan difícil. Es un poco más difícil que manejar las ocurrencias de singleton% y _. (Tenga en cuenta que no es suficiente simplemente escapar de todas las apariciones de paréntesis, porque un paréntesis único se considera literal y no necesita escapar. La lógica se está volviendo un poco más borrosa de lo que puedo manejar sin ejecutar más casos de prueba .)
La expresión en línea se vuelve desordenada
Esa expresión en línea en el SQL se está volviendo más larga y fea. Probablemente podamos hacerlo funcionar, pero el cielo ayuda a la pobre alma que viene detrás y tiene que descifrarlo. Como soy un gran admirador de las expresiones en línea, me inclino a no usar una aquí, principalmente porque no quiero tener que dejar un comentario explicando la razón del desastre y disculpándome por ello.
Una función donde?
Bien, entonces, si no manejamos eso como una expresión en línea en el SQL, la alternativa más cercana que tenemos es una función definida por el usuario. Y sabemos que eso no acelerará las cosas (a menos que podamos definir un índice en él, como podríamos hacerlo con Oracle). Si tenemos que crear una función, podríamos hacerlo mejor en el código que llama al SQL declaración.
Y esa función puede tener algunas diferencias de comportamiento, dependiendo del DBMS y la versión. (Un agradecimiento a todos los desarrolladores de Java tan interesados en poder utilizar cualquier motor de base de datos indistintamente).
Conocimiento del dominio
Es posible que tengamos un conocimiento especializado del dominio de la columna, (es decir, el conjunto de valores permitidos para la columna. Podemos saber a priori que los valores almacenados en la columna nunca contendrán un signo de porcentaje, un guión bajo o un corchete pares. En ese caso, solo incluimos un comentario rápido de que esos casos están cubiertos.
Los valores almacenados en la columna pueden permitir% o _ caracteres, pero una restricción puede requerir que esos valores se escapen, tal vez usando un carácter definido, de modo que los valores sean como la comparación "segura". Una vez más, un comentario rápido sobre el conjunto de valores permitidos, y en particular qué personaje se usa como personaje de escape, y sigue el enfoque de Joel Spolsky.
Pero, sin el conocimiento especializado y una garantía, es importante para nosotros al menos considerar manejar esos oscuros casos de esquina, y considerar si el comportamiento es razonable y "según la especificación".
Otros temas recapitulados
Creo que otros ya han señalado suficientemente algunas de las otras áreas de preocupación comúnmente consideradas:
Inyección de SQL (tomar lo que parece ser información suministrada por el usuario e incluirla en el texto de SQL en lugar de proporcionarla a través de variables de enlace. No es necesario usar variables de enlace, es solo un enfoque conveniente para frustrar con la inyección de SQL. Hay otros maneras de lidiar con eso:
plan optimizador que utiliza exploración de índice en lugar de búsquedas de índice, posible necesidad de una expresión o función para escapar de comodines (posible índice de expresión o función)
el uso de valores literales en lugar de variables de enlace afecta la escalabilidad
Conclusión
Me gusta el enfoque de Joel Spolsky. Es inteligente Y funciona.
Pero tan pronto como lo vi, inmediatamente vi un problema potencial con él, y no es mi naturaleza dejarlo pasar. No quiero criticar los esfuerzos de los demás. Sé que muchos desarrolladores toman su trabajo muy personalmente, porque invierten mucho en él y les importa mucho. Entonces, por favor, comprenda, esto no es un ataque personal. Lo que estoy identificando aquí es el tipo de problema que surge en la producción en lugar de las pruebas.
Sí, me he alejado mucho de la pregunta original. Pero, ¿dónde más dejar esta nota sobre lo que considero un problema importante con la respuesta "seleccionada" para una pregunta?