2010-07-22 11 views
14

Necesito un gurú SQL que me ayude a acelerar mi consulta.La consulta MySQL con subconsulta dependiente lleva demasiado tiempo

Tengo 2 tablas, cantidades y precios. cantidades registra un valor de cantidad entre 2 marcas de tiempo, con 15 minutos de diferencia. los precios registran un precio para una marca de tiempo dada, para un tipo de precio dado y hay un registro de precio 5 por cada 5 minutos.

Necesito 2 calcular el precio total de cada período, p. hora o día, entre dos marcas de tiempo. Esto se calcula por la suma de (cantidad multiplicada por el promedio de los 3 precios en la ventana de cantidad de 15 minutos) en cada período.

Por ejemplo, supongamos que quiero ver el precio total cada hora durante 1 día. El valor del precio total en cada fila en el conjunto de resultados es la suma de los precios totales para cada uno de los cuatro períodos de 15 minutos en esa hora. Y el precio total para cada período de 15 minutos se calcula multiplicando el valor de la cantidad en ese período por el promedio de los 3 precios (uno por cada 5 minutos) en el período de esa cantidad.

Esto es la consulta que estoy usando, y los resultados:

SELECT 
MIN(`quantities`.`start_timestamp`) AS `start`, 
MAX(`quantities`.`end_timestamp`) AS `end`, 
SUM(`quantities`.`quantity` * (
    SELECT AVG(`prices`.`price`) 
    FROM `prices` 
    WHERE `prices`.`timestamp` >= `quantities`.`start_timestamp` 
    AND `prices`.`timestamp` < `quantities`.`end_timestamp` 
    AND `prices`.`type_id` = 1 
)) AS total 
FROM `quantities` 
WHERE `quantities`.`start_timestamp` >= '2010-07-01 00:00:00' 
AND `quantities`.`start_timestamp` < '2010-07-02 00:00:00' 
GROUP BY HOUR( `quantities`.`start_timestamp`); 

+---------------------+---------------------+----------+ 
| start    | end     | total | 
+---------------------+---------------------+----------+ 
| 2010-07-01 00:00:00 | 2010-07-01 01:00:00 | 0.677733 | 
| 2010-07-01 01:00:00 | 2010-07-01 02:00:00 | 0.749133 | 
| 2010-07-01 02:00:00 | 2010-07-01 03:00:00 | 0.835467 | 
| 2010-07-01 03:00:00 | 2010-07-01 04:00:00 | 0.692233 | 
| 2010-07-01 04:00:00 | 2010-07-01 05:00:00 | 0.389533 | 
| 2010-07-01 05:00:00 | 2010-07-01 06:00:00 | 0.335300 | 
| 2010-07-01 06:00:00 | 2010-07-01 07:00:00 | 1.231467 | 
| 2010-07-01 07:00:00 | 2010-07-01 08:00:00 | 0.352800 | 
| 2010-07-01 08:00:00 | 2010-07-01 09:00:00 | 1.447200 | 
| 2010-07-01 09:00:00 | 2010-07-01 10:00:00 | 0.756733 | 
| 2010-07-01 10:00:00 | 2010-07-01 11:00:00 | 0.599467 | 
| 2010-07-01 11:00:00 | 2010-07-01 12:00:00 | 1.056467 | 
| 2010-07-01 12:00:00 | 2010-07-01 13:00:00 | 1.252600 | 
| 2010-07-01 13:00:00 | 2010-07-01 14:00:00 | 1.285567 | 
| 2010-07-01 14:00:00 | 2010-07-01 15:00:00 | 0.442933 | 
| 2010-07-01 15:00:00 | 2010-07-01 16:00:00 | 0.692567 | 
| 2010-07-01 16:00:00 | 2010-07-01 17:00:00 | 1.281067 | 
| 2010-07-01 17:00:00 | 2010-07-01 18:00:00 | 0.652033 | 
| 2010-07-01 18:00:00 | 2010-07-01 19:00:00 | 1.721900 | 
| 2010-07-01 19:00:00 | 2010-07-01 20:00:00 | 1.362400 | 
| 2010-07-01 20:00:00 | 2010-07-01 21:00:00 | 1.099300 | 
| 2010-07-01 21:00:00 | 2010-07-01 22:00:00 | 0.646267 | 
| 2010-07-01 22:00:00 | 2010-07-01 23:00:00 | 0.873100 | 
| 2010-07-01 23:00:00 | 2010-07-02 00:00:00 | 0.546533 | 
+---------------------+---------------------+----------+ 
24 rows in set (5.16 sec) 

necesito la consulta que correr mucho más rápido que esto, y no tendría sin embargo sería posible. He aquí los resultados de EXPLAIN EXTENDIDO ...

+----+--------------------+------------+-------+-------------------+-----------------+---------+-------+-------+----------------------------------------------+ 
| id | select_type  | table  | type | possible_keys  | key    | key_len | ref | rows | Extra          | 
+----+--------------------+------------+-------+-------------------+-----------------+---------+-------+-------+----------------------------------------------+ 
| 1 | PRIMARY   | quantities | range | start_timestamp | start_timestamp | 8  | NULL | 89 | Using where; Using temporary; Using filesort | 
| 2 | DEPENDENT SUBQUERY | prices  | ref | timestamp,type_id | type_id   | 4  | const | 22930 | Using where         | 
+----+--------------------+------------+-------+-------------------+-----------------+---------+-------+-------+----------------------------------------------+ 
2 rows in set, 3 warnings (0.00 sec) 

me di cuenta de la sub consulta dependiente no está utilizando el campo de marca de tiempo en la clave y la consulta está escaneando un montón de filas.

¿Alguien puede ayudarme a hacer que esto funcione mucho más rápido?

Estas son las sentencias SQL necesarias para crear el esquema y llenarlo con una gran cantidad de datos (2) meses de valor

# Create prices table 

CREATE TABLE `prices` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `timestamp` datetime NOT NULL, 
    `type_id` int(11) NOT NULL, 
    `price` float(8,2) NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `timestamp` (`timestamp`), 
    KEY `type_id` (`type_id`) 
) ENGINE=MyISAM; 

# Create quantities table 

CREATE TABLE `quantities` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `start_timestamp` datetime NOT NULL, 
    `end_timestamp` datetime NOT NULL, 
    `quantity` float(7,2) NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `start_timestamp` (`start_timestamp`), 
    KEY `end_timestamp` (`end_timestamp`) 
) ENGINE=MyISAM; 

# Insert first 2 rows into prices, one for each of 2 types, starting 64 days ago 

INSERT INTO `prices` (`id`, `timestamp`, `type_id`, `price`) VALUES 
(NULL, DATE_SUB(CURDATE(), INTERVAL 64 DAY), '1', RAND()), 
(NULL, DATE_SUB(CURDATE(), INTERVAL 64 DAY), '2', RAND()); 

# Fill the prices table with a record for each type, for every 5 minutes, for the next 64 days 

INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 32 DAY), `type_id`, RAND() FROM prices; 
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 16 DAY), `type_id`, RAND() FROM prices; 
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 8 DAY), `type_id`, RAND() FROM prices; 
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 4 DAY), `type_id`, RAND() FROM prices; 
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 2 DAY), `type_id`, RAND() FROM prices; 
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 1 DAY), `type_id`, RAND() FROM prices; 
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 12 HOUR), `type_id`, RAND() FROM prices; 
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 6 HOUR), `type_id`, RAND() FROM prices; 
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 3 HOUR), `type_id`, RAND() FROM prices; 
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 90 MINUTE), `type_id`, RAND() FROM prices; 
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 45 MINUTE), `type_id`, RAND() FROM prices; 
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 20 MINUTE), `type_id`, RAND() FROM prices; 
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 10 MINUTE), `type_id`, RAND() FROM prices; 
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_ADD(`timestamp`, INTERVAL 5 MINUTE), `type_id`, RAND() FROM prices; 
INSERT INTO prices (`timestamp`, `type_id`, `price`) SELECT DATE_SUB(`timestamp`, INTERVAL 5 MINUTE), `type_id`, RAND() FROM prices WHERE MOD((TIME_TO_SEC(`timestamp`) - TIME_TO_SEC(CONCAT(DATE_SUB(CURDATE(), INTERVAL 64 DAY), ' 00:00:00'))), 45 *60) = 0 AND `timestamp` > CONCAT(DATE_SUB(CURDATE(), INTERVAL 64 DAY), ' 00:00:00'); 

# Insert first row into quantities, start timestamp is 64 days ago, end timestamp is start timestamp plus 15 minutes 

INSERT INTO `quantities` (`id`, `start_timestamp`, `end_timestamp`, `quantity`) VALUES (NULL, DATE_SUB(CURDATE(), INTERVAL 64 DAY), DATE_SUB(CURDATE(), INTERVAL '63 23:45' DAY_MINUTE), RAND()); 

# Fill the quantities table with a record for each 15 minute period for the next 64 days 

INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 32 DAY), DATE_ADD(`end_timestamp`, INTERVAL 32 DAY), RAND() FROM quantities; 
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 16 DAY), DATE_ADD(`end_timestamp`, INTERVAL 16 DAY), RAND() FROM quantities; 
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 8 DAY), DATE_ADD(`end_timestamp`, INTERVAL 8 DAY), RAND() FROM quantities; 
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 4 DAY), DATE_ADD(`end_timestamp`, INTERVAL 4 DAY), RAND() FROM quantities; 
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 2 DAY), DATE_ADD(`end_timestamp`, INTERVAL 2 DAY), RAND() FROM quantities; 
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 1 DAY), DATE_ADD(`end_timestamp`, INTERVAL 1 DAY), RAND() FROM quantities; 
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 12 HOUR), DATE_ADD(`end_timestamp`, INTERVAL 12 HOUR), RAND() FROM quantities; 
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 6 HOUR), DATE_ADD(`end_timestamp`, INTERVAL 6 HOUR), RAND() FROM quantities; 
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 3 HOUR), DATE_ADD(`end_timestamp`, INTERVAL 3 HOUR), RAND() FROM quantities; 
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 90 MINUTE), DATE_ADD(`end_timestamp`, INTERVAL 90 MINUTE), RAND() FROM quantities; 
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 45 MINUTE), DATE_ADD(`end_timestamp`, INTERVAL 45 MINUTE), RAND() FROM quantities; 
INSERT INTO `quantities` (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_ADD(`start_timestamp`, INTERVAL 15 MINUTE), DATE_ADD(`end_timestamp`, INTERVAL 15 MINUTE), RAND() FROM quantities; 
INSERT INTO quantities (`start_timestamp`, `end_timestamp`, `quantity`) SELECT DATE_SUB(`start_timestamp`, INTERVAL 15 MINUTE), DATE_SUB(`end_timestamp`, INTERVAL 15 MINUTE), RAND() FROM quantities WHERE MOD((TIME_TO_SEC(`start_timestamp`) - TIME_TO_SEC(CONCAT(DATE_SUB(CURDATE(), INTERVAL 64 DAY), ' 00:00:00'))), 45 * 60) = 0 AND `start_timestamp` > CONCAT(DATE_SUB(CURDATE(), INTERVAL 64 DAY), ' 00:00:00'); 
+5

+1 para DDL y datos de muestra – Unreason

+0

La única forma de mejorar el rendimiento es volver a escribir la subconsulta como JOIN. – Naktibalda

+0

Creo que eso es lo que JochenJung también recomendó a continuación, pero estamos teniendo problemas para que genere los resultados correctos y todavía nos está tomando el mismo tiempo. ¿Tienes alguna idea Naktibalda? – neilcrookes

Respuesta

5

aquí es mi primer intento Éste es sucio y utiliza las siguientes propiedades en los datos:

  • hay tres 5 minutos precios para cada trimestre en las cantidades (si esto es violado en los datos de la consulta no funcionará)
  • aviso para cada uno y cardinalidad de tres, esto no está garantizado por las comprobaciones de integridad de datos para por lo tanto lo llamo sucio
  • también no es flexible a los cambios en períodos

de consulta 1:

SELECT sql_no_cache 
    min(q.start_timestamp) as start, 
    max(q.end_timestamp) as end, 
    sum((p1.price + p2.price + p3.price)/3*q.quantity) as total 
FROM 
    quantities q join 
    prices p1 on q.start_timestamp = p1.timestamp and p1.type_id = 1 join 
    prices p2 on p2.timestamp = adddate(q.start_timestamp, interval 5 minute) and p2.type_id = 1 join 
    prices p3 on p3.timestamp = adddate(q.start_timestamp, interval 10 minute) and p3.type_id = 1 
WHERE 
    q.start_timestamp between '2010-07-01 00:00:00' and '2010-07-01 23:59:59' 
GROUP BY hour(q.start_timestamp); 

Esto arroja resultados en 0.01 segundos en mi máquina de prueba lenta, donde la consulta original se ejecuta en ~ 6 segundos y la consulta de gnarf en ~ 0.85 segundos (todas las consultas siempre se prueban con SQL_NO_CACHE palabra clave que no reutiliza los resultados, pero en una cálida base de datos).

EDIT: Aquí es una versión que no es sensible a las filas que hacen falta en el lado de los precios consulta 1a

SELECT sql_no_cache 
    min(q.start_timestamp) as start, 
    max(q.end_timestamp) as end, 
    sum((COALESCE(p1.price,0) + COALESCE(p2.price,0) + COALESCE(p3.price,0))/( 
     3 - 
     COALESCE(p1.price-p1.price,1) - 
     COALESCE(p2.price-p2.price,1) - 
     COALESCE(p3.price-p3.price,1) 
     ) 
     *q.quantity) as total 
FROM 
    quantities q LEFT JOIN 
    prices p1 on q.start_timestamp = p1.timestamp and p1.type_id = 1 LEFT JOIN 
    prices p2 on p2.timestamp = adddate(q.start_timestamp, interval 5 minute) and p2.type_id = 1 LEFT JOIN 
    prices p3 on p3.timestamp = adddate(q.start_timestamp, interval 10 minute) and p3.type_id = 1 
WHERE 
    q.start_timestamp between '2010-07-01 00:00:00' and '2010-07-01 23:59:59' 
GROUP BY hour(q.start_timestamp); 

Edit2: Consulta 2: Aquí es una mejora directa, y un enfoque diferente, a la consulta con mínimos cambios que trae el tiempo execuction a ~ 0,22 seg en mi máquina

SELECT sql_no_cache 
MIN(`quantities`.`start_timestamp`) AS `start`, 
MAX(`quantities`.`end_timestamp`) AS `end`, 
SUM(`quantities`.`quantity` * (
    SELECT AVG(`prices`.`price`) 
    FROM `prices` 
    WHERE 
    `prices`.`timestamp` >= '2010-07-01 00:00:00' 
    AND `prices`.`timestamp` < '2010-07-02 00:00:00' 
    AND `prices`.`timestamp` >= `quantities`.`start_timestamp` 
    AND `prices`.`timestamp` < `quantities`.`end_timestamp` 
    AND `prices`.`type_id` = 1 
)) AS total 
FROM `quantities` 
WHERE `quantities`.`start_timestamp` >= '2010-07-01 00:00:00' 
AND `quantities`.`start_timestamp` < '2010-07-02 00:00:00' 
GROUP BY HOUR( `quantities`.`start_timestamp`); 

eso es MySQL 5.1, creo que he leído que en este tipo de 5,5 Thi ng (fusionar índices) estará disponible para el planificador de consultas. Además, si puede hacer que start_timestamp y timestamp se relacionen a través de una clave externa que debería permitir este tipo de consultas correlacionadas para hacer uso de índices (pero para esto necesitaría modificar el diseño y establecer algún tipo de tabla de línea de tiempo que luego podría referenciarse por cantidades y precios ambos).

Consulta 3: Finalmente, la última versión, que lo hace en ~ 0,03 segundos, pero debe ser tan robusta y flexible como la consulta 2

SELECT sql_no_cache 
MIN(start), 
MAX(end), 
SUM(subtotal) 
FROM 
(
SELECT sql_no_cache 
q.`start_timestamp` AS `start`, 
q.`end_timestamp` AS `end`, 
AVG(p.`price` * q.`quantity`) AS `subtotal` 
FROM `quantities` q 
LEFT JOIN `prices` p ON p.timestamp >= q.start_timestamp AND 
         p.timestamp < q.end_timestamp AND 
         p.timestamp >= '2010-07-01 00:00:00' AND 
         p.`timestamp` < '2010-07-02 00:00:00' 
WHERE q.`start_timestamp` >= '2010-07-01 00:00:00' 
AND q.`start_timestamp` < '2010-07-02 00:00:00' 
AND p.type_id = 1 
GROUP BY q.`start_timestamp` 
) forced_tmp 
GROUP BY hour(start); 

NOTA: No se olvide de quitar SQL_NO_CACHE palabras clave en producción

Hay muchos trucos contra la intuición aplicados en las consultas anteriores (a veces las condiciones repetidas en la condición de combinación aceleran las consultas, a veces las ralentizan). Mysql es un gran RDBMS increíble y muy rápido cuando se trata de consultas relativamente simples, pero cuando aumenta la complejidad, es fácil ejecutar los escenarios anteriores.

Así que en general, aplicar el siguiente principio para establecer mis expectativas en cuanto a la realización de una consulta:

  • si el conjunto de resultados de base tiene < 1000 filas y luego consulta debe hacer su negocio en ~ 0,01 seg (el conjunto de resultados base es el número de filas que determinan funcionalmente el conjunto resultante)

En este caso particular, empiezas con menos de 1000 filas (todos los precios y cantidades en un día, con 15 minutos de precisión) y de eso debería ser capaz de calcular los resultados finales.

+0

usted es una leyenda, muchas gracias. La consulta 2 arroja resultados perfectos en 0.0039 segundos y la consulta 3 también arroja resultados perfectos en 0.1655 segundos – neilcrookes

+0

@neilcrookes. De nada. ¿Puedes confirmar que Query 2 se ejecuta más rápido que Query 3 en tu máquina? (Inicialmente había Query 1A no marcada, que marqué correctamente ahora. También debería permitir que DB indexe índices, generalmente ejecuto consultas con 'sql_no_cache' varias veces para el benchmarking). – Unreason

+0

(ya no pudo editar el primer comentario, por lo que crear uno nuevo), usted es una leyenda, muchas gracias. La consulta 1a arroja resultados perfectos en 0.0039 segundos y la consulta 2 también arroja resultados perfectos en 0.1655 segundos. Query 3 sufre el mismo problema que la consulta de @ gnarf porque no devuelve una fila donde no hay precios en esa hora y las horas de inicio y final corresponden a los registros de precios más antiguos y más recientes en esa hora, pero regresa en 0.0144 segundos . La consulta 1a es la ganadora. Gracias de nuevo. Eres un salvavidas. – neilcrookes

0

No sé si es más rápido, pero trata de éste:

SELECT 
    MIN(`quantities`.`start_timestamp`) AS `start`, 
    MAX(`quantities`.`end_timestamp`) AS `end`, 
    (`quantities`.`quantity` * AVG (`prices`.`price`) * COUNT (`prices`.`price`)) AS `total` 
FROM `quantities` 
LEFT JOIN `prices` 
    ON `prices`.`timestamp` >= `quantities`.`start_timestamp` 
    AND `prices`.`timestamp` < `quantities`.`end_timestamp` 
WHERE `quantities`.`start_timestamp` >= '2010-07-01 00:00:00' 
    AND `quantities`.`start_timestamp` < '2010-07-02 00:00:00' 
    AND `prices`.`type_id` = 1 
GROUP BY HOUR( `quantities`.`start_timestamp`); 

Compara también los resultados, porque la lógica es un poco diferente.

no hago SUM (quantety * AVG (precio)

debo hacer AVG (precio) * COUNT (precio) * quantety

+0

Gracias JochenJung pero me aparece ERROR 1111 (HY000) : Uso no válido de la función de grupo – neilcrookes

+0

Olvidé un corchete de cierre. Por favor, intenta de nuevo. – JochenJung

+0

Ahora obtengo ERROR 1305 (42000): FUNCIÓN COUNT no existe ... ¡raro! – neilcrookes

2

Esto debería devolver los mismos resultados y llevar a cabo un poco más rápido:

SELECT 
    MIN(`quantities`.`start_timestamp`) AS `start`, 
    MAX(`quantities`.`end_timestamp`) AS `end`, 
    SUM(`quantities`.`quantity` * `prices`.`price`) 
    * COUNT(DISTINCT `quantities`.`id`) 
/COUNT(DISTINCT `prices`.`id`) 
    AS total 
FROM `quantities` 
JOIN `prices` ON `prices`.`timestamp` >= `quantities`.`start_timestamp` 
    AND `prices`.`timestamp` < `quantities`.`end_timestamp` 
    AND `prices`.`type_id` = 1 
WHERE `quantities`.`start_timestamp` >= '2010-07-01 00:00:00' 
    AND `quantities`.`start_timestamp` < '2010-07-02 00:00:00' 
GROUP BY HOUR( `quantities`.`start_timestamp`); 

Dado que no se puede calcular AVG() dentro del SUM(), que tenía que hacer algunas interesantes COUNT(DISTINCT) para calcular el número de prices devuelto por quantities. me pregunto si t su le da los mismos resultados con los datos "reales" ...

Usando JOIN:

+----+-------------+------------+-------+-------------------------------+-----------------+---------+------+-------+----------+----------------------------------------------+ 
| id | select_type | table  | type | possible_keys     | key    | key_len | ref | rows | filtered | Extra          | 
+----+-------------+------------+-------+-------------------------------+-----------------+---------+------+-------+----------+----------------------------------------------+ 
| 1 | SIMPLE  | quantities | range | start_timestamp,end_timestamp | start_timestamp | 8  | NULL | 89 | 100.00 | Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | prices  | ALL | timestamp,type_id    | NULL   | NULL | NULL | 36862 | 62.20 | Using where; Using join buffer    | 
+----+-------------+------------+-------+-------------------------------+-----------------+---------+------+-------+----------+----------------------------------------------+ 

vs.la misma consulta sólo se suma a la LEFTJOIN

+----+-------------+------------+-------+-------------------+-----------------+---------+-------+-------+----------+----------------------------------------------+ 
| id | select_type | table  | type | possible_keys  | key    | key_len | ref | rows | filtered | Extra          | 
+----+-------------+------------+-------+-------------------+-----------------+---------+-------+-------+----------+----------------------------------------------+ 
| 1 | SIMPLE  | quantities | range | start_timestamp | start_timestamp | 8  | NULL | 89 | 100.00 | Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | prices  | ref | timestamp,type_id | type_id   | 4  | const | 22930 | 100.00 |            | 
+----+-------------+------------+-------+-------------------+-----------------+---------+-------+-------+----------+----------------------------------------------+ 

interesante que LEFT se elimina por completo el end_timestamp como una clave que sea posible, y cambia las claves seleccionadas tanto, por lo que se tarda 15 veces más largo ...

This reference page podría ayudarlo un poco más si desea ver la especificación de sugerencias de índice para SUS UNIDAS

+0

+1 Esto es bueno, también agrega índices compuestos en (start_timestamp , end_timestamp) y on (type_id, timestamp) deberían ayudar. Sin embargo, creo que podré reducirlo a ~ 0.01 segundos – Unreason

+0

@Unreason --- * rasca la cabeza * dices +1, pero nadie ha votado aún;) --- '' --- I ' Estoy interesado en ver cómo lo llevas tan lejos! – gnarf

+0

Gracias Gnarf, esto es casi perfecto. Se está ejecutando en aproximadamente 0,4 segundos en mi máquina, pero los resultados son diferentes a mi consulta original. La razón por la que creo es porque se divide por COUNT ('prices''price'), que con la cláusula GROUP y estos datos serán 4 filas de cantidad * 3 filas de precios = 12, pero si divide por 3, entonces genera los mismos resultados que mi consulta original. El problema es que no quiero el código 3 en la consulta, pero no puedo entender cuál es el SQL para derivar ese valor de los datos. Una vez que esa parte esté ordenada, todo será perfecto. Alguna idea muy apreciada? – neilcrookes

0

Recuerde, solo porque tenga índices en sus columnas no significa necesariamente que se ejecutarán más rápido. Tal como está, el índice creado es para cada columna individual, que, si solo estuviera limitando los datos en una columna, devolvería los resultados bastante rápido.

Así que para tratar de evitar "Uso filesort" (que lo que necesita hacer tanto como sea posible), tal vez tratar el siguiente índice:

CREATE INDEX start_timestamp_end_timestamp_id ON quantities (start_timestamp,end_timestamp,id); 

Y algo similar para la tabla de precios (la combinación de la 3 persona índices que tienen en 1 el índice para la búsqueda rápida)

Un excelente recurso que explica con gran detalle y cómo optimizar sus índices (y lo que los diferentes Explicar de decir, y lo que debe aspirar a) es: http://hackmysql.com/case1

+0

Gracias AcidRaZor, pero agregar este índice y uno en la tabla de precios no hizo mucho para mejorar el rendimiento ni en mi consulta original ni en la que sugirió @gnarf. – neilcrookes

+0

Valió la pena intentarlo:) Sin embargo, todavía estaría recomiendo leer a través del sitio web que cité también. Entran en más detalles sobre cómo se puede mejorar el rendimiento con sus consultas – AcidRaZor

+0

va a hacer, gracias – neilcrookes

Cuestiones relacionadas