2009-06-30 13 views
5

Esta consulta SQL me repugna. No lo escribí, pero es una gran causa de problemas en nuestros servidores. Estoy dispuesto a dividirlo en varias consultas y hacer parte del procesamiento a través de PHP (como, el RAND()).Optimizar esta consulta SQL

$sql = "SELECT a.code, a.ad_id, a.position, a.type, a.image, a.url, a.height, a.width 
    FROM " . AD_TABLE ." a, " . USER_GROUP_TABLE . " g 
    WHERE (a.max_views >= a.views OR a.max_views = '0') 
    AND (FIND_IN_SET(" .$forum_id. ", a.show_forums) > 0 OR a.show_all_forums = '1') 
    AND g.user_id = " . $user->data['user_id'] . " 
    AND FIND_IN_SET(g.group_id, a.groups) 
    AND FIND_IN_SET(" . $user->data['user_rank'] . ", a.ranks) 
    AND a.start_time < " . time() . " 
    AND a.end_time > " . time() . " 
    AND (a.clicks <= a.max_clicks OR a.max_clicks = '0') 
    ORDER BY rand()"; 

Yeesh, me siento repulsivo que después de pegar ...

EDIT: A continuación se muestra los resultados de la "explicar" en una consulta de ejemplo en el formato anterior, delimitado por comas:

"id","select_type","table","type","possible_keys","key","key_len","ref","rows","Extra" 
1,"SIMPLE","g","ref","user_id","user_id","3","const",6,"Using temporary; Using filesort" 
1,"SIMPLE","a","ALL","max_views","","","",10,"Using where" 

es

+2

¿Estás haciendo una pregunta, o tratando de obtener algún SQL optimizado sin pagar por ello? – Mathew

+0

¿Te importaría hacer una EXPLAIN SELECT y publicar los resultados? – alexn

+2

El asco es el motor de la solución. – RedFilter

Respuesta

6

tiene tres grandes problemas aquí:

  1. FIND_IN_SET.

    No es sargable, un índice no puede hacerlo más rápido. Cree una tabla (o tablas) de relaciones de muchos a muchos.

    • a.start_time < GETDATE() AND a.end_time > GETDATE()

    MySQL no es bueno en la optimización de eso. Puede mantenerse lapsos como cajas de geometría y crear un SPATIAL INDEX sobre ellos, esto será mucho más rápido (aunque menos legible)

    • ORDER BY RAND()

    Si está usando este para muestrear datos (es decir, que Don 't necesidad de todas las filas, sino más bien un pequeño subconjunto aleatorio), hay una manera más eficiente de hacer esto, se describe en este artículo en mi blog:

1

estoy adivinando el problema está en las fechas.

AND a.start_time < " . time() . " 
AND a.end_time > " . time() . " 

Trataría de poner índices en estos campos y ver si eso ayuda. La comparación de fechas hace que la base de datos se compare con cada fila de la tabla.

4
  • Usted no tienen unirse a una explícita en esta consulta entre los cuadros A y G: que sólo están relacionadas por FIND_IN_SET (g.group_id, a.groups)
  • ¿Qué tan grande es su se establece en "a.groups", es decir, ¿la cadena csv contiene un ID de grupo la mayoría de las veces?
  • Si el 99% de los casos contiene solo 1 grupo, crea un bucle foreach sobre "a.groups" en php y ejecuta una combinación real (o probablemente elimine tu user_group_table por completo) de la consulta.
    • Para la minoría de los casos en los que tiene más de 1 miembro de grupo, la consulta seguirá funcionando bien con una unión explícita.

Esto añadirá algo más de código en su clase PHP/función.

+0

Ok, ¿qué tal un intento más? Esto podría ser muy simple. Usa "y a.groups like". "%". g.group_id. "%" Esto evaluará a "y 'grp1, grp2, grp4' como '% grp1%'" y obtendrá el golpe. – blispr

0

Si ejecuta esta mucho .. tratar de utilizar variables de enlace (en lugar de la concatenación de cadenas) .. lo que la consulta no tiene que ser analizado cada vez ..

edición: Sorry..didn' ver la etiqueta MYSQL A menos que haya cambiado recientemente, no creo que a MySQL le gusten las declaraciones preparadas. ORACLE, por otro lado, los AMA.

Cuestiones relacionadas