Estoy construyendo una consulta SQL en C #. Diferirá según algunas condiciones almacenadas como variables en el 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, pero probar 1 = 1 no parece elegante. Si no lo usé, tendría que recordar y verificar cada vez si la palabra clave "donde" ya se agregó o no a la consulta.
¿Existe una mejor solución?
42 = 42
;-)Select 42
consultas que estábamos recibiendo. (no es divertido intentar rastrear la fuente)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 eso usas1 = 1
. El motor de la base de datos lo optimiza de todos modos, por lo que, si bien puede verse feo, es, con mucho, la forma más fácil de resolver el problema.Respuestas:
Guarde las condiciones en una lista:
fuente
ToArray()
no es necesaria con .NET 4 ya que hay una sobrecarga que acepta cualquieraIEnumerable<string>
.Una solución es simplemente no escribir consultas manualmente agregando cadenas. Puede usar un ORM, como Entity Framework , y con LINQ to Entities usar las características que el lenguaje y el marco le ofrecen:
fuente
PrintResults(query)
consulta generada se usará en Dapper como consulta!Un poco exagerado en este caso simple, pero he usado un código similar a este en el pasado.
Crea una función
Úselo así
De esta manera, si no se encuentran condiciones, ni siquiera se molesta en cargar una declaración where en la consulta y le ahorra al servidor SQL un microsegundo de procesamiento de la cláusula where basura cuando analiza la declaración SQL.
fuente
Hay otra solución, que puede que tampoco sea elegante, pero funciona y resuelve el problema:
Por:
SELECT * FROM Table1
,SELECT * FROM Table1 WHERE cond1
AND condN
fuente
WHERE
si no hay predicados; el 1 = 1 existe específicamente para evitar eso.String query = "SELECT * FROM Table1";
ystring jointer = " WHERE ";
?WHERE
¿AND
deben colocarse las s entre condiciones?string joiner
línea constring joiner = " WHERE ";
y dejar lajoiner = " AND ";
línea en paz.Solo haz algo como esto:
Es seguro para la inyección de SQL y en mi humilde opinión , es bastante limpio. El
Remove()
simplemente quita el últimoAND
;Funciona tanto si no se han establecido condiciones, si se ha establecido una o si se han establecido varias.
fuente
conditions != null
siempre es asítrue
, ya que lo inicializa con""
(a menos que en C #"" == null
). Probablemente debería ser un cheque, siconditions
no está vacío… ;-)Solo agregue dos líneas al final.
P.ej
se convertirá en
Mientras
se convertirá en
=====================================
Gracias por señalar un defecto de esta solución:
"Esto podría interrumpir la consulta si, por cualquier motivo, una de las condiciones contiene el texto" 1 = 1 Y "o" DONDE 1 = 1 ". Este podría ser el caso si la condición contiene una subconsulta o intenta verificar si alguna columna contiene este texto, por ejemplo. Tal vez esto no sea un problema en su caso, pero debe tenerlo en cuenta ... "
Para deshacernos de este problema, necesitamos distinguir el "principal" DONDE 1 = 1 y los de la subconsulta, lo cual es fácil:
Simplemente haga que el DONDE "principal" sea especial: agregaría un signo "$"
Entonces aún agregue dos líneas:
fuente
"1=1 AND "
o" WHERE 1=1 "
. Este podría ser el caso si la condición contiene una subconsulta o intenta verificar si alguna columna contiene este texto, por ejemplo. Tal vez esto no sea un problema en su caso, pero debe tenerlo en cuenta ...Utilizar este:
fuente
QuerySub
En mi opinión, la búsqueda de cadenas no es mejor ni peor que usar elwhere 1=1
truco. Pero es una contribución reflexiva.... FROM SOMETABLE WHERE
; entonces elTrimEnd
realmente reduciría esto a... FROM SOMETABL
. Si esto fue realmente unStringBuilder
(que debería ser si tiene tanta manipulación de cadenas o más) puede simplementeQuery.Length -= "WHERE ".Length;
.¿Por qué no utilizar un generador de consultas existente? Algo como Sql Kata .
Admite condiciones complejas, uniones y subconsultas.
funciona con Sql Server, MySql y PostgreSql.
fuente
La solución literal más rápida a lo que estás preguntando que puedo pensar es esta:
No parece elegante, seguro, a lo que le recomendaría que consulte la recomendación de CodeCaster de usar un ORM. Pero si piensa en lo que esto está haciendo aquí, realmente no le preocupa "desperdiciar" 4 caracteres de memoria, y es muy rápido para una computadora mover un puntero 4 lugares.
Si tiene tiempo para aprender a usar un ORM, realmente podría valer la pena. Pero con respecto a esto, si está tratando de evitar que esa condición adicional golpee la base de datos SQL, esto lo hará por usted.
fuente
Si se trata de SQL Server , puede hacer que este código sea mucho más limpio.
Esto también asume un número conocido de parámetros, lo que puede ser una suposición pobre cuando pienso en las posibilidades.
En C #, usaría:
Y luego en el lado de SQL:
fuente
Dependiendo de la condición, podría ser posible utilizar lógica booleana en la consulta. Algo como esto :
fuente
Me gusta la interfaz fluida de Stringbuilder, así que hice algunos ExtensionMethods.
fuente
En mi humilde opinión, creo que su enfoque es incorrecto:
Consultar la base de datos concatenando cadenas NUNCA es una buena idea (riesgo de inyección de SQL y el código puede romperse fácilmente si realiza algunos cambios en otro lugar).
Puedes usar un ORM (yo uso NHibernate ) o al menos usar
SqlCommand.Parameters
Si absolutamente desea usar la concatenación de cadenas, usaría un
StringBuilder
(es el objeto correcto para la concatenación de cadenas):Como último pensamiento,
Where 1=1
es realmente feo pero SQL Server lo optimizará de todos modos.fuente
SELECT * FROM Table1 WHERE AND Col1=0
no parece correcto, que es el punto deWHERE 1=1
.Dapper SqlBuilder es una opción bastante buena. Incluso se usa en producción en StackOverflow.
Lea la entrada del blog de Sam al respecto. .
Hasta donde yo sé, no es parte de ningún paquete de Nuget, por lo que deberá copiar y pegar su código en su proyecto o descargar la fuente de Dapper y compilar el proyecto SqlBuilder. De cualquier manera, también deberá hacer referencia a Dapper para la
DynamicParameters
clase.fuente
Veo que esto se usa todo el tiempo en Oracle mientras se construye SQL dinámico dentro de los procedimientos almacenados . Lo uso en consultas mientras exploro problemas de datos también para hacer que el cambio entre diferentes filtros de datos sea más rápido ... Simplemente comente una condición o vuelva a agregarla fácilmente.
Encuentro que es bastante común y bastante fácil de entender para alguien que revisa su código.
fuente
Realización con métodos de extensión.
fuente
Usando la
string
función también puede hacerlo de esta manera:Personalmente, me parece fácil eliminar los elementos condicionales al final, ya que su posición es fácil de predecir.
fuente
Pensé en una solución que, bueno, quizás sea algo más legible:
Simplemente no estoy seguro de si el intérprete de SQL también optimizará la
Col1 = Col1
condición (impresa cuandocondition1
es falsa).fuente
Aquí hay una forma más elegante:
fuente
Como se ha dicho, la creación de SQL mediante concatenación nunca es una buena idea . No solo por la inyección SQL. Sobre todo porque es feo, difícil de mantener y totalmente innecesario . Tienes que ejecutar tu programa con rastreo o depuración para ver qué SQL genera. Si usa QueryFirst (descargo de responsabilidad: que escribí), la tentación infeliz se elimina y puede comenzar directamente a hacerlo en SQL.
Esta página tiene una cobertura completa de las opciones de TSQL para agregar predicados de búsqueda de forma dinámica. La siguiente opción es útil para situaciones en las que desea dejar la elección de combinaciones de predicados de búsqueda a su usuario.
QueryFirst le da C # nulo a db NULL, por lo que simplemente llama al método Execute () con nulos cuando sea apropiado, y todo simplemente funciona. <opinion> ¿Por qué los desarrolladores de C # son tan reacios a hacer cosas en SQL, incluso cuando es más simple? Mente aturdida. </opinion>
fuente
Para pasos de filtrado más largos, StringBuilder es el mejor enfoque, como muchos dicen.
en tu caso iría con:
fuente
Conciso, elegante y dulce, como se muestra en la imagen de abajo.
fuente