sql >> Base de Datos >  >> RDS >> Mysql

Consulta MySQL muy específica que quiero mejorar.

Si tiene un índice con created como columna principal, MySQL puede hacer un escaneo inverso. Si tiene un período de 24 horas que no tiene ningún evento, podría estar devolviendo una fila que NO es de ese período. Para asegurarse de obtener una fila en ese período, realmente necesitaría incluir un límite inferior en el created columna también, algo como esto:

SELECT * FROM `eventos`
 WHERE ... 
   AND `created` <  FROM_UNIXTIME( {$timestamp} )
   AND `created` >= DATE_ADD(FROM_UNIXTIME( {$timestamp} ),INTERVAL -24 HOUR)
 ORDER BY `created` DESC
 LIMIT 1

Creo que la gran clave del rendimiento aquí es un índice con created como la columna principal, junto con todas (o la mayoría) de las otras columnas a las que se hace referencia en la cláusula WHERE, y asegúrese de que su consulta utilice ese índice.

Si necesita un intervalo de tiempo diferente, hasta el segundo, este enfoque podría generalizarse fácilmente.

SELECT * FROM `eventos`
 WHERE ... 
   AND `created` <  DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL  0*{$nsecs} SECOND)
   AND `created` >= DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -1*{$nsecs} SECOND)
 ORDER BY `created` DESC
 LIMIT 1

Según su código, parece que los períodos de 24 horas están limitados a un tiempo arbitrario... si la función de tiempo regresa, p. 1341580800 ('2012-07-06 13:20'), entonces sus diez períodos serían desde las 13:20 de un día determinado hasta las 13:20 del día siguiente.

(NOTA:asegúrese de que si su parámetro es un número entero de marca de tiempo de Unix, la base de datos lo interprete correctamente).

Podría ser más eficiente extraer las diez filas en una sola consulta. Si hay una garantía de que la 'marca de tiempo' es única, entonces es posible crear una consulta de este tipo, pero el texto de la consulta será considerablemente más complejo que el que tiene ahora. Podríamos jugar con obtener MAX(timestamp_) dentro de cada período, y luego volver a unirlo para obtener la fila... pero eso va a ser muy complicado.

Si tuviera que intentar extraer las diez filas, probablemente intentaría ir con UNION ALL enfoque, no muy bonito, pero por lo menos se podría afinar.

SELECT p0.*
  FROM ( SELECT * FROM `eventos` WHERE ... 
            AND `created` <  DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL  0*24 HOUR)
            AND `created` >= DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -1*24 HOUR)
          ORDER BY `created` DESC LIMIT 1
       ) p0 
 UNION ALL           
SELECT p1.*
  FROM ( SELECT * FROM `eventos` WHERE ... 
            AND `created` <  DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -1*24 HOUR)
            AND `created` >= DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -2*24 HOUR)
          ORDER BY `created` DESC LIMIT 1
       ) p1 
 UNION ALL           
SELECT p2.*
  FROM ( SELECT * FROM `eventos` WHERE ... 
            AND `created` <  DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -2*24 HOUR)
            AND `created` >= DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -3*24 HOUR)
          ORDER BY `created` DESC LIMIT 1
       ) p2 
 UNION ALL           
SELECT p3.*
  FROM ...

Nuevamente, esto podría generalizarse, para pasar una cantidad de segundos como argumento. Reemplace HORA con SEGUNDO y reemplace '24' con un parámetro de vinculación que tenga una cantidad de segundos.

Es bastante extenso, pero debería funcionar bien.

Otra forma realmente desordenada y complicada de recuperar esto en un solo conjunto de resultados sería usar una vista en línea para obtener la marca de tiempo final de los diez períodos, algo como esto:

     SELECT p.period_end
       FROM (SELECT DATE_ADD(t.t_,INTERVAL -1 * i.i_* {$nsecs} SECOND) AS period_end
               FROM (SELECT FROM_UNIXTIME( {$timestamp} ) AS t_) t
               JOIN (SELECT 0 AS i_
                     UNION ALL SELECT 1
                     UNION ALL SELECT 2
                     UNION ALL SELECT 3
                     UNION ALL SELECT 4
                     UNION ALL SELECT 5
                     UNION ALL SELECT 6
                     UNION ALL SELECT 7
                     UNION ALL SELECT 8
                     UNION ALL SELECT 9
                    ) i
            ) p

Y luego únelo a tu mesa...

  ON `created` < p.period_end
 AND `created` >= DATE_ADD(p.period_end,INTERVAL -1 * {$nsecs} SECOND)

Y retire MAX (creado) para cada período GROUP BY p.period_end, envuélvalo en una vista en línea.

Y luego únelo a tu tabla para obtener cada fila.

Pero eso es muy, muy complicado, difícil de entender y no es probable que sea más rápido (o más eficiente) que lo que ya está haciendo. La mayor mejora que podría hacer es el tiempo que lleva ejecutar 9 de sus consultas.