2008-09-18 42 views
57

¿Cómo creo un índice en la parte de la fecha del campo DATETIME?¿Cómo se crea un índice en la parte de la fecha del campo DATETIME en MySql

mysql> SHOW COLUMNS FROM transactionlist; 
+-------------------+------------------+------+-----+---------+----------------+ 
| Field    | Type    | Null | Key | Default | Extra   | 
+-------------------+------------------+------+-----+---------+----------------+ 
| TransactionNumber | int(10) unsigned | NO | PRI | NULL | auto_increment | 
| WagerId   | int(11)   | YES | MUL | 0  |    | 
| TranNum   | int(11)   | YES | MUL | 0  |    | 
| TranDateTime  | datetime   | NO |  | NULL |    | 
| Amount   | double   | YES |  | 0  |    | 
| Action   | smallint(6)  | YES |  | 0  |    | 
| Uid    | int(11)   | YES |  | 1  |    | 
| AuthId   | int(11)   | YES |  | 1  |    | 
+-------------------+------------------+------+-----+---------+----------------+ 
8 rows in set (0.00 sec) 

TranDateTime se utiliza para guardar la fecha y hora de la transacción como ocurre

Mi tabla tiene más de 1.000.000 de registros en el mismo y la declaración

SELECT * FROM transactionlist where date(TranDateTime) = '2008-08-17' 

lleva mucho tiempo.

EDIT:

Tener un vistazo a esta entrada del blog sobre "Why MySQL’s DATETIME can and should be avoided"

+3

advertencia comentario para el enlace que sugirió una mirada: La publicación está escrita con tal emoción y rabia que casi roza el punto de infantilismo. Y el escritor no está rechazando ninguna crítica, mientras sigue mencionando que está detrás de lo que dijo, pero su punto es cada vez menor. Pero aún así, no es una pérdida de tiempo, si lees los comentarios. – kommradHomer

Respuesta

50

Si mal no recuerdo, se ejecutará una exploración de toda la tabla porque está pasando la columna a través de una función. MySQL ejecutará obedientemente la función para cada columna, omitiendo el índice, ya que el optimizador de consultas no puede conocer realmente los resultados de la función.

Lo que me gustaría hacer es algo como:

SELECT * FROM transactionlist 
WHERE TranDateTime BETWEEN '2008-08-17 00:00:00' AND '2008-08-18 23:59:59'; 

Eso te dará todo lo que pasó en 2008-08-17, y todo lo que pasó exactamente 2008-08-18 00:00:00. Si eso es un problema, puede cambiar el segundo término a '2008-08-17 23:59:59' y obtener 2008-08-17.

+0

que es realmente muy efectivo ... – Arfeen

+1

solía pensar en este uso como un atajo para 'YYYY-MM-DD 00:00:00' – kommradHomer

+3

Sé que esta es una respuesta antigua, pero me siento obligado a señale que MySQL utiliza la comparación de cadenas para 'DATETIME'; su consulta arroja los resultados correctos y no incluye las filas con 'TranDateTime = 2008-08-18 00: 00: 00'. – Arth

0

¿Qué significa 'explicar' decir? (Ejecutar EXPLAIN SELECT * FROM transactionlist donde la fecha (TranDateTime) = '2008-08-17')

Si no está utilizando el índice debido a la función date(), una consulta rango debe correr rápido:

SELECT * FROM transactionlist donde TranDateTime> = '2008-08-17' AND TranDateTime < '2008-08-18'

+1

Si usa date() no alcanzará el índice. Mysql no puede usar índices dentro de llamadas a funciones como esa. – JBB

3

No conozco los detalles de mySql, pero ¿cuál es el daño al solo indexar el campo de fecha? ¿en su totalidad?

Entonces sólo la búsqueda:

select * from translist 
    where TranDateTime > '2008-08-16 23:59:59' 
     and TranDateTime < '2008-08-18 00:00:00' 

Si los índices son los árboles B o algo más que es razonable, esto hay que dejarse encontrar rápidamente.

9

No pretendo sonar lindo, pero una manera simple sería agregar una nueva columna que solo contenga la parte de la fecha y el índice sobre eso.

+0

Sí, y agregue una columna con solo la parte de tiempo, y elimine el DATETIME por completo. – JBB

+0

mi solución actual es agregar otra llamada de campo 'fecha' y cuando actualizo el TranDateTime la fecha también se actualiza. Ahora tengo un índice en la 'fecha' y la consulta es mucho más rápida por mi tabla aumentó en tamaño en + -5% –

0

En lugar de hacer un índice basado en una función (si eso es posible en mysql) haga que su cláusula where haga una comparación de rango. Algo así como:

Dónde TranDateTime> '2008-08-17 00:00:00 ' y TranDateTime < '2008-08-17 11:59:59')

De este modo, el DB usa el índice en TranDateTime (hay uno, ¿no?) para hacer la selección.

2

Valeriy Kravchuk en una solicitud de función para este mismo problema en el sitio de MySQL dijo que utiliza este método.

"Mientras tanto puede usar columnas de caracteres para almacenar valores DATETIME como cadenas, con solo los primeros N caracteres indexados. Con un uso cuidadoso de desencadenadores en MySQL 5 puede crear una solución razonablemente robusta basada en esta idea".

Puede escribir una rutina bastante fácil para agregar esta columna, y luego con activadores mantener esta columna sincronizada. El índice en esta columna de cadena debe ser bastante rápido.

8

No puede crear un índice solo en la parte de la fecha. ¿Hay alguna razón por la que tienes que hacerlo?

Incluso si pudiera crear un índice solo en la parte de la fecha, el optimizador probablemente todavía no la usaría para la consulta anterior.

creo que usted encontrará que

SELECT * FROM transactionlist WHERE TranDateTime BETWEEN '2008-08-17' AND '2008-08-18' 

es eficiente y hace lo que quiere.

0

No conozco los detalles de mySQL, pero ¿cuál es el daño al solo indexar el campo de fecha en su totalidad?

Si utiliza magic funcional para * trees, hashes, ... se ha ido, porque para obtener valores debe llamar a la función. Pero, debido a que no conoce los resultados futuros, debe hacer un análisis completo de la tabla.

No hay nada que agregar.

Tal vez te refieres a algo así como índices calculados (calculados?) ... pero hasta la fecha, solo he visto esto en Intersystems Caché. No creo que haya un caso en las bases de datos relacionales (AFAIK).

Una buena solución, en mi opinión, es la siguiente (ejemplo clintp actualizada):

SELECT * FROM translist 
WHERE TranDateTime >= '2008-08-17 00:00:00.0000' 
    AND TranDateTime < '2008-08-18 00:00:00.0000' 

Ya sea que utilice o 00:00:00.000000:00 en mi opinión no hace ninguna diferencia (He usado generalmente en este formato)

0

Crea un nuevo campo con solo las fechas convert(datetime, left(date_field,10)) y luego indexa eso.

1

La única y buena solución que funciona bastante bien es utilizar la marca de tiempo como tiempo, en lugar de fecha y hora. Se almacena como INT y se indiza lo suficiente. Personalmente me encontré con ese problema en la tabla de transacciones, que tiene alrededor de un millón de registros y se ralentizó mucho, finalmente señalé que esto era causado por un campo indexado incorrecto (datetime). Ahora funciona muy rápido.

-2

¿Por qué nadie sugirió usar LIKE? ¿Eso no hace el trabajo también? ¿Será tan rápido como BETWEEN?

SELECT * FROM transactionlist where TranDateTime LIKE '2008-08-17%' 
1

datetime ME GUSTA something% will not catch the index either.

Utilice esto: DONDE datetime_field> = curdate();
que cogerá el índice,
y cubrir hoy: 00: 00: 00 hasta hoy: 23: 59: 59
Listo.

5

Otra opción (relevante para ver 7.5.3 y superior) es crear una columna generada/virtual basada en la columna de fecha y hora, luego indizarla.

CREATE TABLE `table` (
`my_datetime` datetime NOT NULL, 
`my_date` varchar(12) GENERATED ALWAYS AS (DATE(`my_daetime`)) STORED, 
KEY `my_idx` (`my_date`) 
) ENGINE=InnoDB; 
+0

¿por qué está almacenado y no es virtual? – 0x13a

+0

Si desea indexar, debe estar almacenado. Sin índice puede ser virtual –

+0

thx, imaginé, aunque me confundí con este artículo https://www.percona.com/blog/2016/03/04/virtual -columns-in-mysql-and-mariadb/ – 0x13a

Cuestiones relacionadas