2010-12-02 6 views
5

considere la siguiente consulta:optimización de una consulta SQL para evitar la mesa de exploración completa del

SELECT * FROM Transactions 
WHERE day(Stamp - interval 3 hour) = 1; 

El columna sello en el Transacciones tabla es un TIMESTAMP y hay un índice en ella. ¿Cómo podría cambiar esta consulta para evitar escaneos completos de tabla? (Es decir, utilizando sello fuera del día() función)

Gracias!

+0

No estoy pidiendo "índices de funciones", no existen. Más bien me gustaría transformar esta consulta de la misma manera que podría transformar "SELECT * FROM table WHERE sqrt (column) = 2" en "SELECT * FROM table WHERE column = 4" – emx

Respuesta

7

Esta es la forma en que lo haría:

añadir algunos campos adicionales: año, mes, día o incluso hora, minuto, dependiendo del tráfico que se espera. Luego construya un disparador para rellenar los campos adicionales, tal vez restando el intervalo de 3 horas por adelantado. Finalmente construya algún índice en los campos adicionales.

+0

Gracias, no es una opción. – emx

+4

MySQL no admite índices de función. La solución de Massimog es la única alternativa para especificar cada intervalo de fechas posible; sin embargo, dado que su consulta probablemente extraerá 1/30 de las filas, usar una búsqueda de índice no será significativamente más rápido que usar un escaneo completo de la tabla. – symcbean

+0

Puede que tenga razón en la consideración del rendimiento, nunca pensé en eso realmente. Más interesado en transformar mi consulta. – emx

1

Si el objetivo es sólo para evitar escaneos completos de tabla y que tiene una clave principal (digamos llamado PK) para transacciones, considerar la adición de cubrir índice

ALTER TABLE Transactions ADD INDEX cover_1 (PK, Stamp) 

Entonces

SELECT * FROM Transactions WHERE PK IN (SELECT PK FROM Transactions 
WHERE day(Stamp - interval 3 hour) = 1 
) 

Esta consulta debe no use escaneos completos de tabla (sin embargo, el optimizador puede decidir usar escaneo completo, si el número de filas en la tabla es pequeño o por cualquier otra razón estadística :))

Mejor manera ma y be es usar una tabla temporal en lugar de una subconsulta.

0

Calcular el valor de sello deseada por separado antes de ejecutar la consulta principal, es decir

Paso 1 - calcular el valor de marca deseada

Paso 2 - ejecutar una consulta en la cotización> (valor calculado)

Como no hay cálculos en el paso 2, debería poder usar su índice.

+0

Yo también lo deseo, pero esto no funciona – ajreal

1

A menudo puede volver a escribir la función para que tenga algo que se parece a WHERE Stamp=XXXX y XXXX es alguna expresión. Puede crear una serie de declaraciones BETWEEN para cada mes, WHERE Stamp BETWEEN timestamp('2010-01-01 00:00:00') AND timestamp ('2010-01-01 23:59:59') OR Stamp BETWEEN ..., pero no estoy seguro de que esto use el índice en este caso. Construiría una columna que era el día del mes como sugiere @petr.

0

Si lo entiendo correctamente, básicamente desea devolver todas las filas donde el sello cae el primero de cada mes (habiendo restado las 3 horas)? Si (y este es un gran si), tiene una ventana fija de, digamos, los últimos 6 meses, puede enumerar 6 pruebas de rango. Pero aún así, no estoy seguro de que el acceso indexado sea más rápido de todos modos.

select * 
    from transactions 
where stamp between timestamp '2010-06-01 03:00:00' and timestamp '2010-06-02 02:59:59' 
    or stamp between timestamp '2010-07-01 03:00:00' and timestamp '2010-07-02 02:59:59' 
    or stamp between timestamp '2010-08-01 03:00:00' and timestamp '2010-08-02 02:59:59' 
    or stamp between timestamp '2010-09-01 03:00:00' and timestamp '2010-09-02 02:59:59' 
    or stamp between timestamp '2010-10-01 03:00:00' and timestamp '2010-10-02 02:59:59' 
    or stamp between timestamp '2010-11-01 03:00:00' and timestamp '2010-11-02 02:59:59' 
    or stamp between timestamp '2010-12-01 03:00:00' and timestamp '2010-12-02 02:59:59'; 

NB! No estoy seguro de cómo funciona la parte en milisegundos de la marca de tiempo. Es posible que deba rellenarlo en consecuencia.

0

Volviendo a trabajar la respuesta de petr para evitar la cláusula IN y para hacerla para MyISAM o InnoDB.

Para MyISAM

ALTER TABLE Transactions ADD INDEX cover_1 (PK, Stamp) 

O, para InnoDB, donde el PK está implícitamente incluido en cada índice,

ALTER TABLE Transactions ADD INDEX Stamp (Stamp) 

Entonces

SELECT * 
FROM Transactions LEFT JOIN 
    (
    SELECT PK 
    FROM Transactions 
    WHERE DAYOFMONTH(Stamp - interval 3 hour) = 1 
) a ON Transactions.PK=a.PK 

La subconsulta tendrá un índice sólo ejecución, y la consulta externa solo extraerá las filas de la tabla por la que entró a .PK.

Cuestiones relacionadas