2012-02-17 13 views
5

tengo siguientes tablas:Optimización de consultas MySQL con SUM, rango de fecha y Agrupar por

CREATE TABLE IF NOT EXISTS stats (
    date date NOT NULL DEFAULT '0000-00-00', 
    cid int(8) NOT NULL DEFAULT '0', 
    v bigint(15) NOT NULL DEFAULT '0', 
    c bigint(15) NOT NULL DEFAULT '0', 
    a bigint(15) NOT NULL DEFAULT '0', 
PRIMARY KEY (date,cid), 
KEY date (date), 
KEY cid (cid), 
KEY date_cid_vca (date,cid,v,c,a) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 

esta tabla tiene filas

y

CREATE TABLE IF NOT EXISTS camp (
id int(8) NOT NULL AUTO_INCREMENT, 
name varchar(80) NOT NULL DEFAULT '', 
PRIMARY KEY (id,name) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

Esta tabla tiene filas

Tengo la siguiente consulta:

SELECT 
    c.id, 
    c.name, 
    SUM(s.v) AS sumv, 
    SUM(s.c) AS sumc, 
    GREATEST(((SUM(s.c)/SUM(s.v))*100.00), 0.00) AS cratio, 
    SUM(s.a) AS suma, 
    GREATEST(((SUM(s.a)/SUM(s.c))*100.00), 0.00) AS aratio 
FROM 
    stats s, camp c 
WHERE 
    s.date >= '2012-02-01' AND 
    s.date <= '2012-02-29' AND 
    c.id=s.cid 
GROUP BY s.cid; 

EXPLAIN muestra:

+----+-------------+-------+-------+-------------------------------+--------------+---------+---------------------+---------+-----------------------------------------------------------+ 
| id | select_type | table | type | possible_keys     | key   | key_len | ref     | rows | Extra              | 
+----+-------------+-------+-------+-------------------------------+--------------+---------+---------------------+---------+-----------------------------------------------------------+ 
| 1 | SIMPLE  | s  | range | PRIMARY,date,cid,date_cid_vca | date_cid_vca | 3  | NULL    | 1010265 | Using where; Using index; Using temporary; Using filesort | 
| 1 | SIMPLE  | c  | ref | PRIMARY      | PRIMARY  | 4  | db.s.cid   |  1 | Using index            | 
+----+-------------+-------+-------+-------------------------------+--------------+---------+---------------------+---------+-----------------------------------------------------------+ 

problema es que la consulta tarda unos 50 segundos para completar a pesar de que utiliza los índices. ¿Hay alguna otra forma de optimizar la consulta?

Gracias!

Respuesta

4

Has hecho bien en optimizar esta consulta con tus índices. Supongo que realmente tiene más de 1 millón de filas en stats en ese rango de fechas. Desafortunadamente, unir (y luego agrupar) 1 millón de filas, incluso con un índice de cobertura, es mucho pedir en una base de datos. Para un mejor rendimiento, necesitará reforzar el hardware, comenzar a desnormalizar (poner camp dentro de stats para evitar la unión), o mantener los totales para cada campamento en lugar de calcularlo sobre la marcha.

Editar

Dado que la eliminación de la 1 millón + Une parecía haber hecho un gran impacto, puede intentar algo como esto:

SELECT c.*, a.* FROM 
(SELECT 
    SUM(s.v) AS sumv, 
    SUM(s.c) AS sumc, 
    GREATEST(((SUM(s.c)/SUM(s.v))*100.00), 0.00) AS cratio, 
    SUM(s.a) AS suma, 
    GREATEST(((SUM(s.a)/SUM(s.c))*100.00), 0.00) AS aratio, 
    s.cid 
FROM 
    stats s 
WHERE 
    s.date >= '2012-02-01' 
    AND s.date <= '2012-02-29' 
GROUP BY s.cid) a 
JOIN 
    camp c 
    ON c.id = a.cid 

Esta consulta al unirse en el conjunto de resultados más pequeño.

+0

Creo que el hardware no es el problema. El servidor tiene 4 CPU con 8 núcleos (32 núcleos, 64 subprocesos) y 64 GB de RAM. Tiene 6x discos SSD en RAID 10. my.cnf está optimizado al máximo (basado en tuning-primer.sh).¿Puedes dar más información sobre desnormalización? ¡Gracias! – Paxxil

+0

He intentado eliminar join y luego extraer datos del campo con una consulta separada dentro del bucle de datos de la consulta principal. Supongo que es el camino a seguir porque ahora el tiempo de ejecución total se redujo enormemente. Tarda alrededor de 10 segundos en completarse. – Paxxil

+0

Suena como lindo, carnoso, hardware. :) –

0

Puede crear una combinación interna con su tabla C, y en la combinación usar las condiciones de fechas, debe reducir el tiempo de su consulta.

Probablemente podrías hacer más optimizaciones, pero ese es el primer 1 que pude ver.

+0

¿me puede dar un ejemplo? gracias – Paxxil

1

La siguiente consulta debe permitir que se utilice de manera más eficiente los índices

SELECT 
    c.id, 
    c.name, 
    SUM(s.v) AS sumv, 
    SUM(s.c) AS sumc, 
    GREATEST(((SUM(s.c)/SUM(s.v))*100.00), 0.00) AS cratio, 
    SUM(s.a) AS suma, 
    GREATEST(((SUM(s.a)/SUM(s.c))*100.00), 0.00) AS aratio 
FROM 
    camp c 
INNER JOIN 
    stats s 
ON 
    s.cid = c.id 
    AND s.date BETWEEN '2012-02-01' AND '2012-02-29' 

GROUP BY c.id; 

También yo consideraría eliminar la clave date_cid_vca ya que es sólo contiene toda la tabla y por lo tanto no es particularmente útil. La consulta anterior debe usar el PK para unir las filas de las estadísticas con el campamento basado en fecha y cid, y si bien es difícil estar 100% seguro sin acceso a su base de datos, estoy bastante seguro de que lo anterior mejorará sus tiempos de respuesta

+0

He intentado su sugerencia, pero desafortunadamente toma alrededor de 5 segundos más ejecutar su versión de la consulta. Sin índice date_cid_vca toma el doble de tiempo. – Paxxil