Estou construindo alguma consulta SQL em C #. Ele será diferente dependendo de algumas condições armazenadas como variáveis no código.
string Query="SELECT * FROM Table1 WHERE 1=1 ";
if (condition1)
Query += "AND Col1=0 ";
if (condition2)
Query += "AND Col2=1 ";
if (condition3)
Query += "AND Col3=2 ";
Funciona, mas testar 1 = 1 não parece elegante. Se eu não usasse, teria que lembrar e verificar sempre se a palavra-chave "where" já foi adicionada ou não à consulta.
Existe uma solução mais agradável?
42 = 42
;-)Select 42
perguntas que estávamos recebendo. (não era divertido tentar rastrear a fonte)If I didn't use it, I would have to remember and check every time if "where" keyword was already added or not to the query
- É por isso que você usa1 = 1
. O mecanismo de banco de dados o otimiza de qualquer maneira, portanto, embora possa parecer feio, é de longe a maneira mais fácil de resolver o problema.Respostas:
Salve as condições em uma lista:
fonte
ToArray()
não é necessário com o .NET 4, pois há uma sobrecarga que aceita qualquer umIEnumerable<string>
.Uma solução é simplesmente não escrever consultas manualmente anexando strings. Você poderia usar um ORM, como Entity Framework , e com LINQ to Entities usar os recursos que a linguagem e a estrutura oferecem a você:
fonte
PrintResults(query)
consulta gerada então usaria no dapper como consulta !!Um pouco de exagero neste caso simples, mas já usei um código semelhante a este no passado.
Crie uma função
Use assim
Dessa forma, se nenhuma condição for encontrada, você nem mesmo se preocupa em carregar uma instrução where na consulta e salva o servidor sql um microssegundo de processamento da cláusula where do lixo quando ele analisa a instrução sql.
fonte
Existe outra solução, que também pode não ser elegante, mas funciona e resolve o problema:
Para:
SELECT * FROM Table1
,SELECT * FROM Table1 WHERE cond1
AND condN
fonte
WHERE
se não houver predicados; o 1 = 1 existe especificamente para evitar isso.String query = "SELECT * FROM Table1";
estring jointer = " WHERE ";
?WHERE
osAND
s devem ser colocados entre as condições?string joiner
linha porstring joiner = " WHERE ";
e deixar ajoiner = " AND ";
linha sozinha.Basta fazer algo assim:
É seguro para injeção de SQL e IMHO , é bastante limpo. O
Remove()
simplesmente remove o últimoAND
;Funciona tanto se nenhuma condição tiver sido definida, se uma tiver sido definida ou se várias forem definidas.
fonte
conditions != null
é sempretrue
, conforme você inicializa com""
(a menos que em C #"" == null
). Provavelmente deveria ser um cheque, seconditions
não estiver vazio ... ;-)Basta anexar duas linhas no final.
Por exemplo
se tornará para
Enquanto
se tornará para
===========================================
Obrigado por apontar uma falha desta solução:
"Isso pode interromper a consulta se, por qualquer motivo, uma das condições contiver o texto" 1 = 1 AND "ou" WHERE 1 = 1 ". Este pode ser o caso se a condição contiver uma subconsulta ou tentar verificar se algum coluna contém este texto, por exemplo. Talvez isso não seja um problema no seu caso, mas você deve ter isso em mente ... "
Para nos livrarmos desse problema, precisamos distinguir o "principal" WHERE 1 = 1 e os da subconsulta, o que é fácil:
Basta fazer o "principal" ONDE especial: Eu acrescentaria um sinal "$"
Em seguida, ainda acrescente duas linhas:
fonte
"1=1 AND "
ou" WHERE 1=1 "
. Pode ser o caso se a condição contiver uma subconsulta ou tentar verificar se alguma coluna contém esse texto, por exemplo. Talvez isso não seja um problema no seu caso, mas você deve ter isso em mente ...Usa isto:
fonte
QuerySub
é, em minha opinião, nem melhor nem pior do que usar owhere 1=1
hack. Mas é uma contribuição atenciosa.... FROM SOMETABLE WHERE
; então oTrimEnd
reduziria para... FROM SOMETABL
. Se este for realmente umStringBuilder
(o que deveria ser se você tiver tanta manipulação de strings ou mais), você pode simplesmenteQuery.Length -= "WHERE ".Length;
.Por que não usar um Query Builder existente? Algo como Sql Kata .
Suporta condições onde complexas, junções e subconsultas.
funciona com Sql Server, MySql e PostgreSql.
fonte
A solução literal mais rápida para o que você está perguntando é esta:
Não parece elegante, claro, para o qual eu recomendaria a recomendação da CodeCaster de usar um ORM. Mas se você pensar sobre o que isso está fazendo aqui, você realmente não está preocupado em 'desperdiçar' 4 caracteres de memória, e é muito rápido para um computador mover um ponteiro 4 lugares.
Se você tiver tempo para aprender como usar um ORM, pode realmente valer a pena. Mas com relação a isso, se você está tentando evitar que essa condição adicional atinja o banco de dados SQL, isso fará isso por você.
fonte
Se este for o SQL Server , você pode tornar esse código muito mais limpo.
Isso também pressupõe um número conhecido de parâmetros, o que pode ser uma suposição ruim quando penso sobre as possibilidades.
Em C #, você usaria:
E então no lado do SQL:
fonte
Dependendo da condição, pode ser possível usar a lógica booleana na consulta. Algo assim :
fonte
Eu gosto da interface fluente do stringbuilder, então fiz alguns ExtensionMethods.
fonte
IMHO, acho que sua abordagem está errada:
Consultar o banco de dados concatenando string NUNCA é uma boa ideia (risco de injeção de SQL e o código pode ser facilmente quebrado se você fizer algumas alterações em outro lugar).
Você pode usar um ORM (eu uso NHibernate ) ou pelo menos usar
SqlCommand.Parameters
Se você realmente deseja usar concatenação de string, eu usaria um
StringBuilder
(é o objeto certo para concatenação de string):Como o último pensamento,
Where 1=1
é realmente feio, mas o SQL Server irá otimizá-lo de qualquer maneira.fonte
SELECT * FROM Table1 WHERE AND Col1=0
não parece correto, e esse é o ponto principalWHERE 1=1
.O Dapper SqlBuilder é uma opção muito boa. É até usado na produção no StackOverflow.
Leia a entrada do blog de Sam sobre isso .
Até onde eu sei, não faz parte de nenhum pacote Nuget, então você precisará copiar e colar seu código em seu projeto ou baixar o código-fonte do Dapper e construir o projeto SqlBuilder. De qualquer forma, você também precisará fazer referência a Dapper para a
DynamicParameters
classe.fonte
Vejo que isso é usado o tempo todo no Oracle ao construir SQL dinâmico em procedimentos armazenados . Eu o uso em consultas enquanto exploro problemas de dados também para tornar mais rápida a alternância entre diferentes filtros de dados ... Basta comentar uma condição ou adicioná-la novamente facilmente.
Acho que é bastante comum e fácil de entender para alguém que está revisando seu código.
fonte
Realização com métodos de extensão.
fonte
Usando a
string
função, você também pode fazer desta forma:Eu pessoalmente acho fácil remover o (s) elemento (s) condicional (is) no final, uma vez que sua posição é fácil de prever.
fonte
Pensei em uma solução que, bem, talvez seja um pouco mais legível:
Só não tenho certeza se o interpretador SQL também otimizará a
Col1 = Col1
condição (impresso quandocondition1
é falso).fonte
Aqui está uma maneira mais elegante:
fonte
Como já foi dito, criar SQL por concatenação nunca é uma boa ideia . Não apenas por causa da injeção de SQL. Principalmente porque é simplesmente feio, difícil de manter e totalmente desnecessário . Você tem que executar seu programa com rastreamento ou depuração para ver qual SQL ele gera. Se você usar QueryFirst (aviso: que eu escrevi) a tentação infeliz é removida, e você pode ir direto ao fazer em SQL.
Esta página tem uma cobertura abrangente de opções de TSQL para adicionar predicados de pesquisa dinamicamente. A opção a seguir é útil para situações em que você deseja deixar a escolha de combinações de predicados de pesquisa para seu usuário.
QueryFirst fornece C # null a db NULL, então você apenas chama o método Execute () com valores nulos quando apropriado e tudo funciona. <opinion> Por que os desenvolvedores de C # são tão relutantes em fazer coisas em SQL, mesmo quando é mais simples. Incrivelmente. </opinion>
fonte
Para etapas de filtragem mais longas, StringBuilder é a melhor abordagem, como muitos dizem.
no seu caso eu iria com:
fonte
Conciso, elegante e doce, como mostra a imagem abaixo.
fonte