A pergunta original era "Como parametrizar uma consulta ..."
Deixe-me declarar aqui, que isso não é uma resposta para a pergunta original. Já existem demonstrações disso em outras boas respostas.
Com isso dito, vá em frente e marque esta resposta, faça voto negativo, marque-a como não uma resposta ... faça o que achar que está certo.
Veja a resposta de Mark Brackett para a resposta preferida que eu (e 231 outras) votaram. A abordagem dada em sua resposta permite 1) o uso eficaz de variáveis de ligação e 2) predicados que são sargáveis.
Resposta selecionada
O que quero abordar aqui é a abordagem dada na resposta de Joel Spolsky, a resposta "selecionada" como a resposta certa.
A abordagem de Joel Spolsky é inteligente. E funciona razoavelmente, exibirá comportamento previsível e desempenho previsível, dados valores "normais" e com os casos de borda normativos, como NULL e a sequência vazia. E isso pode ser suficiente para uma aplicação específica.
Mas, em termos de generalização dessa abordagem, vamos considerar também os casos de canto mais obscuros, como quando a Name
coluna contém um caractere curinga (conforme reconhecido pelo predicado LIKE). O caractere curinga que eu vejo mais comumente usado é %
(um sinal de porcentagem). Então, vamos lidar com isso aqui agora e depois continuar com outros casos.
Alguns problemas com o caractere%
Considere um valor de nome de 'pe%ter'
. (Para os exemplos aqui, eu uso um valor literal de sequência no lugar do nome da coluna.) Uma linha com o valor Name de '' pe% ter 'seria retornada por uma consulta do formulário:
select ...
where '|peanut|butter|' like '%|' + 'pe%ter' + '|%'
Mas essa mesma linha não será retornada se a ordem dos termos da pesquisa for revertida:
select ...
where '|butter|peanut|' like '%|' + 'pe%ter' + '|%'
O comportamento que observamos é meio estranho. Alterar a ordem dos termos da pesquisa na lista altera o conjunto de resultados.
É quase desnecessário dizer que podemos não querer pe%ter
combinar manteiga de amendoim, não importa o quanto ele goste.
Caixa de canto obscura
(Sim, eu concordo que este é um caso obscuro. Provavelmente, provavelmente não será testado. Não esperamos um curinga em um valor de coluna. Podemos assumir que o aplicativo impede que esse valor seja armazenado. Mas Na minha experiência, raramente vi uma restrição de banco de dados que especificamente proibia caracteres ou padrões que seriam considerados curingas no lado direito de um LIKE
operador de comparação.
Corrigindo um buraco
Uma abordagem para corrigir esse buraco é escapar do %
caractere curinga. (Para quem não conhece a cláusula de escape no operador, aqui está um link para a documentação do SQL Server .
select ...
where '|peanut|butter|'
like '%|' + 'pe\%ter' + '|%' escape '\'
Agora podemos combinar o literal%. Obviamente, quando temos um nome de coluna, precisamos escapar dinamicamente do curinga. Podemos usar a REPLACE
função para encontrar ocorrências do %
caractere e inserir um caractere de barra invertida na frente de cada um, assim:
select ...
where '|pe%ter|'
like '%|' + REPLACE( 'pe%ter' ,'%','\%') + '|%' escape '\'
Portanto, isso resolve o problema com o curinga%. Quase.
Escapar da fuga
Reconhecemos que nossa solução introduziu outro problema. O caractere de escape. Vemos que também precisaremos escapar de quaisquer ocorrências do próprio personagem de escape. Desta vez, usamos o! como o caractere de escape:
select ...
where '|pe%t!r|'
like '%|' + REPLACE(REPLACE( 'pe%t!r' ,'!','!!'),'%','!%') + '|%' escape '!'
O sublinhado também
Agora que estamos em um rolo, podemos adicionar outro REPLACE
identificador ao curinga de sublinhado. E só por diversão, desta vez, usaremos $ como o caractere de escape.
select ...
where '|p_%t!r|'
like '%|' + REPLACE(REPLACE(REPLACE( 'p_%t!r' ,'$','$$'),'%','$%'),'_','$_') + '|%' escape '$'
Eu prefiro essa abordagem do que escapar porque funciona no Oracle e no MySQL e no SQL Server. (Eu normalmente uso a barra invertida \ como o caractere de escape, pois esse é o caractere que usamos nas expressões regulares. Mas por que ser restringido pela convenção!
Aqueles suportes chatos
O SQL Server também permite que caracteres curinga sejam tratados como literais colocando-os entre colchetes []
. Portanto, ainda não terminamos a correção, pelo menos para o SQL Server. Como os pares de colchetes têm um significado especial, precisamos escapar deles também. Se conseguirmos escapar adequadamente dos colchetes, pelo menos não teremos que nos preocupar com o hífen -
e o quilate ^
dentro dos colchetes. E podemos deixar qualquer caractere %
e _
dentro dos colchetes escapado, já que basicamente desativamos o significado especial dos colchetes.
Encontrar pares de parênteses não deve ser tão difícil. É um pouco mais difícil do que lidar com as ocorrências de singleton% e _. (Observe que não é suficiente escapar apenas de todas as ocorrências de colchetes, porque um colchete único é considerado literal e não precisa ser escapado. A lógica está ficando um pouco mais confusa do que eu posso suportar sem executar mais casos de teste .)
Expressão em linha fica confusa
Essa expressão embutida no SQL está ficando mais longa e mais feia. Provavelmente, podemos fazer funcionar, mas o céu ajuda a pobre alma que fica para trás e precisa decifrá-la. Por mais que eu seja fã de expressões embutidas, estou inclinado a não usar uma aqui, principalmente porque não quero deixar um comentário explicando o motivo da bagunça e me desculpando.
Uma função onde?
Ok, então, se não tratarmos isso como uma expressão embutida no SQL, a alternativa mais próxima que temos é uma função definida pelo usuário. E sabemos que isso não acelerará as coisas (a menos que possamos definir um índice, como poderíamos com o Oracle.) Se precisarmos criar uma função, é melhor fazer isso no código que chama SQL declaração.
E essa função pode ter algumas diferenças de comportamento, dependentes do DBMS e da versão. (Um grito para todos os desenvolvedores de Java, que desejam tanto usar qualquer mecanismo de banco de dados de maneira intercambiável.)
Conhecimento de domínio
Podemos ter conhecimento especializado do domínio para a coluna (ou seja, o conjunto de valores permitidos aplicados à coluna. Podemos saber a priori que os valores armazenados na coluna nunca conterão um sinal de porcentagem, sublinhado ou colchete Nesse caso, apenas incluímos um comentário rápido de que esses casos são abordados.
Os valores armazenados na coluna podem permitir% ou _ caracteres, mas uma restrição pode exigir que esses valores sejam escapados, talvez usando um caractere definido, de modo que os valores sejam LIKE comparáveis "seguros". Novamente, um comentário rápido sobre o conjunto permitido de valores e, em particular, qual caractere é usado como caractere de escape, e siga a abordagem de Joel Spolsky.
Porém, sem o conhecimento especializado e uma garantia, é importante considerar pelo menos lidar com esses casos obscuros de esquina e considerar se o comportamento é razoável e "de acordo com a especificação".
Outras questões recapituladas
Acredito que outros já apontaram suficientemente algumas das outras áreas de preocupação comumente consideradas:
Injeção de SQL (pegar o que pareceria ser informações fornecidas pelo usuário e incluí-las no texto SQL em vez de fornecê-las através de variáveis de ligação. O uso de variáveis de ligação não é necessário, é apenas uma abordagem conveniente para impedir a injeção de SQL. Existem outras maneiras de lidar com isso:
plano de otimizador usando varredura de índice em vez de buscas de índice, possível necessidade de uma expressão ou função para escapar caracteres curinga (possível índice na expressão ou função)
o uso de valores literais no lugar de variáveis de ligação afeta a escalabilidade
Conclusão
Eu gosto da abordagem de Joel Spolsky. É esperto. E isso funciona.
Mas assim que o vi, imediatamente vi um problema em potencial, e não é da minha natureza deixá-lo escapar. Não quero criticar os esforços dos outros. Sei que muitos desenvolvedores levam o trabalho muito para o lado pessoal, porque investem muito nele e se preocupam muito com isso. Então, por favor, entenda, este não é um ataque pessoal. O que estou identificando aqui é o tipo de problema que surge na produção, em vez de testar.
Sim, fui longe da pergunta original. Mas onde mais deixar esta nota sobre o que considero uma questão importante com a resposta "selecionada" para uma pergunta?