2009-09-02 11 views
27

Ok Necesito crear una consulta basada en alguna entrada del usuario para filtrar los resultados.¿Qué tan mala es mi consulta?

La consulta básicamente es algo como esto:

SELECT * FROM my_table ORDER BY ordering_fld; 

Hay cuatro cuadros de texto en el que los usuarios pueden elegir para filtrar los datos, lo que significa que tendría que construir dinámicamente una cláusula "WHERE" en él para el primer filtro utilizado y luego las cláusulas "AND" para cada filtro subsiguiente ingresado.

Como soy demasiado perezoso para hacer esto, acabo de hacer que cada filtro sea una cláusula "AND" y coloque una cláusula "WHERE 1" en la consulta de forma predeterminada.

así que ahora tengo:

SELECT * FROM my_table WHERE 1 {AND filters} ORDER BY ordering_fld; 

Así que mi pregunta es, he hecho algo que afectará negativamente al rendimiento de mi consulta o buggered cualquier otra cosa en modo alguno que debería estar preocupado de forma remota?

+0

Pregunta muy interesante –

+10

¿Mi consulta parece grande en esto? –

+6

¿Soy yo o Evernoob extremadamente valiente? Nunca me gustaría preguntarle a un sitio lleno de otros desarrolladores (que son notoriamente obstinados) qué tan malo era mi código. –

Respuesta

37

MySQL optimizará su 1 de distancia.

que acaba de ejecutar esta consulta en mi base de datos de prueba:

EXPLAIN EXTENDED 
SELECT * 
FROM t_source 
WHERE 1 AND id < 100 

y me dio la siguiente description:

select `test`.`t_source`.`id` AS `id`,`test`.`t_source`.`value` AS `value`,`test`.`t_source`.`val` AS `val`,`test`.`t_source`.`nid` AS `nid` from `test`.`t_source` where (`test`.`t_source`.`id` < 100) 

Como se puede ver, hay 1 en absoluto.

La documentación en WHERE clause optimization en MySQL menciona esto:

  • plegado constante:

    (a<b AND b=c) AND a=5 
    -> b>5 AND b=c AND a=5 
    
  • Constant condición eliminación (necesario debido a plegado constante):

    (B>=5 AND B=5) OR (B=6 AND 5=5) OR (B=7 AND 5=6) 
    -> B=5 OR B=6 
    

Nota 5 = 5 y 5 = 6 partes en el ejemplo anterior.

+0

+1 para probarlo, usando EXPLAIN y buscando documentación – knittl

+8

'@ knittl': Si cada desarrollador usara' EXPLAIN' y buscara documentación, ¡el mundo sería un lugar mucho mejor! – Quassnoi

+0

+1 por tratar de hacer del mundo un lugar más agradable :) – waqasahmed

2

para mejorar el rendimiento, usan índices de columna en los campos de escuchar "WHERE"

2

estándar de inyección SQL Aviso legal aquí ...

Una cosa que podría hacer, para evitar la inyección de SQL ya que se sabe que es sólo cuatro parámetros es usar un procedimiento almacenado donde pasa valores para los campos o NULL. No estoy seguro de la sintaxis MySQL proc almacenado, pero la consulta se reduciría a

SELECT * 
    FROM my_table 
WHERE Field1 = ISNULL(@Field1, Field1) 
    AND Field2 = ISNULL(@Field2, Field2) 
    ... 
ORDRE BY ordering_fld 
+0

Eso tiende a evitar el uso de índices en el campo1, etc., también debería usar COALESCE en lugar de ISNULL –

+0

El formato correcto sería @Field IS NULL O t.col = @Field. Es una búsqueda de índice desperdiciado o escaneo de tabla de lo contrario. –

8

Usted puede explicar su consulta:
http://dev.mysql.com/doc/refman/5.0/en/explain.html

y ver si se hace algo diferente, cosa que dudo. Yo usaría 1 = 1, solo para que quede más claro.

Es posible que desee agregar LIMIT 1000 o algo así, cuando no se usan parámetros y la tabla aumenta, ¿realmente desea devolver todo?

+0

'@ KM': de acuerdo con las etiquetas, esto debería ser' LIMIT 1000' :) – Quassnoi

+0

@Quassnoi, gracias, estaba pensando en mysql la primera vez que respondí, pero cuando edité y agregué la parte _TOP 1000_, me pareció que el servidor sql . –

4

Si hay una buena forma en el idioma elegido para evitar crear SQL usted mismo, utilícelo en su lugar. Me gustan Python y Django, y el ORM de Django hace que sea muy fácil filtrar los resultados en función de la entrada del usuario.

Si se ha comprometido a construir el SQL usted mismo, asegúrese de desinfectar las entradas del usuario contra la inyección de SQL, y trate de encapsular el desarrollo de SQL en un módulo separado de su lógica de filtro.

Además, el rendimiento de la consulta no debe ser su problema hasta que se convierta en un problema, que probablemente no ocurra hasta que tenga miles o millones de filas. Y cuando llega el momento de optimizar, agregar algunos índices en las columnas utilizadas para WHERE y JOIN es muy útil.

5

WHERE 1 es una expresión constante y determinista que será "optimizada" por cualquier motor de DB decente.

2

Hemos estado haciendo algo similar hace no demasiado tiempo y no estamos algunas cosas que hemos observado:

  • La creación de los índices en las columnas que se filtraban (posiblemente), un mejor rendimiento
  • La parte WHERE 1 puede omitirse por completo si los filtros no se utilizan. (no estoy seguro de si se aplica a tu caso) No hace la diferencia, pero "se siente" bien.
  • inyección SQL no se debe olvidar

Además, si 'sólo' tiene 4 filtros, se puede construir un procedimiento almacenado y pase los valores nulos y comprobar por ellos. (Al igual que n8wrl sugirió en el ínterin)

+0

No estoy demasiado preocupado por las inyecciones de SQL, lo estoy manejando.Era más, solo tenía curiosidad sobre la aceptabilidad de lo que había hecho. – Evernoob

2

Eso funcionará - algunas consideraciones:

SQL Acerca de forma dinámica en general, algunas bases de datos (Oracle, al menos) en caché los planes de ejecución de consultas, por lo que si al final ejecutando la misma consulta muchas veces, no tendrá que volver a empezar desde el principio. Si utiliza SQL construido dinámicamente, está creando una consulta diferente cada vez, por lo que a la base de datos le parecerá 100 consultas diferentes en lugar de 100 ejecuciones de la misma consulta.

Probablemente solo necesite medir el rendimiento para averiguar si funciona lo suficientemente bien para usted.

¿Necesita todas las columnas? La especificación expresa de ellos es probablemente mejor que usar * de todos modos porque:

  • Se puede ver visualmente lo que las columnas están siendo devueltos
  • Si agregar o quitar columnas de la tabla más adelante, que no va a cambiar su interfaz de
+0

Oracle comprueba la caché según el contenido de la consulta enviada, no el hecho de que un usuario ejecute una determinada función o procedimiento. –

2

No está mal, no sabía este fragmento para deshacerse de la pregunta '¿es el primer filtro 3'?

Tho debería avergonzarse de su código (^^), no hace nada para el rendimiento ya que cualquier DB Engine lo optimizará.

+0

jaja, ¿por qué debería avergonzarme de mi código? si el motor de DB lo optimiza, no hay daño en el lado de DB y mi lógica de negocio ahora es más clara y más legible para el siguiente tipo. Entonces, ¿dónde está la vergüenza? – Evernoob

+0

Porque agrega "1 = 1", que es un código inútil, que es malo ... Inofensivo pero malo. Pero todos hacen eso ^^ –

2

La única razón por la que he usado WHERE 1 = 1 es para SQL dinámico; es un truco para facilitar la adición de las cláusulas WHERE usando AND .... No es algo que incluiría en mi SQL de lo contrario: no afecta en absoluto la consulta en general, ya que siempre se evalúa como verdadera y no afecta a la (s) tabla (s) involucradas, por lo que no hay búsquedas de índice o escaneos de tablas basados ​​en eso.

yo no puedo hablar de lo MySQL trata criterios opcionales, pero sé que el uso de los siguientes:

WHERE (@param IS NULL OR t.column = @param) 

... es la forma típica de manejo de parámetros opcionales. COALESCE e ISNULL no son ideales porque la consulta todavía está utilizando índices (o peor, escaneos de tabla) basados ​​en un valor centinela. El ejemplo que proporcioné no llegará a la mesa a menos que se haya proporcionado un valor.

Dicho esto, mi experiencia con Oracle (9i, 10g) ha demostrado que no maneja muy bien [WHERE (@param IS NULL OR t.column = @param)]. Vi una gran ganancia de rendimiento convirtiendo el SQL en dinámico y usé variables CONTEXT para determinar qué agregar. Mi impresión de SQL Server 2005 es que se manejan mejor.

2

por lo general he hecho algo como esto:

for(int i=0; i<numConditions; i++) { 
    sql += (i == 0 ? "WHERE " : "AND "); 
    sql += dbFieldNames[i] + " = " + safeVariableValues[i]; 
} 

realiza la consulta generado un poco más limpio.

2

Una alternativa a veces uso es la construcción de la cláusula donde un una matriz y luego unirlos:

my @wherefields; 
foreach $c (@conditionfields) { 
    push @wherefields, "$c = ?", 
} 

my $sql = "select * from table"; 
if(@wherefields) { $sql.=" WHERE " . join (" AND ", @wherefields); } 

Lo anterior está escrito en Perl, pero la mayoría de los idiomas tienen algún tipo de unen Funciton.