Cómo se crea un índice en la parte fecha del campo DATETIME en MySQL


¿Cómo puedo crear un índice en la parte fecha del campo DATETIME?

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 usa para guardar la fecha y hora de una transacción a medida que sucede

Mi Tabla tiene más de 1,000,000 registros en ella y la declaración

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

Toma mucho tiempo.

EDITAR:

Echa un vistazo a esta entrada de blog sobre " Por qué DATETIME de MySQL puede y debe evitarse "

 57
Author: fancyPants, 2008-09-18

13 answers

Si no recuerdo mal, eso ejecutará un análisis de tabla completa porque estás pasando la columna a través de una función. MySQL ejecutará obedientemente la función para todas y cada una de las columnas, omitiendo el índice ya que el optimizador de consultas no puede conocer realmente los resultados de la función.

Lo que haría es algo como:

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

Eso debería darte todo lo que pasó en 2008-08-17, y todo lo que pasó exactamente en 2008-08-18 00:00:00. Si eso es un problema, podrías cambie el segundo término a '2008-08-17 23:59: 59' y apenas consiga 2008-08-17.

 52
Author: Michael Johnson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-09 21:59:48

No quiero sonar lindo, pero una manera simple sería agregar una nueva columna que solo contuviera la parte de fecha e índice en eso.

 9
Author: Mike Tunnicliffe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-18 18:22:34

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

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

Creo que encontrarás que

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

Es eficiente y hace lo que quieres.

 8
Author: MarkR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-18 19:03:18

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

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;
 5
Author: Liran Brimer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-14 15:13:59

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

Entonces solo busca:

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

Si los índices son árboles b u otra cosa que sea razonable, estos deben ser encontrados rápidamente.

 3
Author: Clinton Pierce,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-18 18:21:23

Valeriy Kravchuk en una solicitud de característica para este mismo problema en el sitio MySQL dijo que use 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 los disparadores en MySQL 5 puede crear una solución razonablemente robusta basada en esta idea."

Podría escribir una rutina bastante fácil de agregar esta columna, y luego con disparadores mantener esta columna sincronizada. El índice en esta columna de cadena debería ser bastante rápida.

 2
Author: Ray Jenkins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-18 18:25:19

La única y buena solución que funciona bastante bien es usar timestamp como tiempo, en lugar de datetime. Se almacena como INT y se indexa lo suficientemente bien. Personalmente me encontré con tal problema en la tabla de transacciones, que tiene alrededor de millones de registros y se ralentizó duro, finalmente señalé que esto causado por mal campo indexado (datetime). Ahora corre muy rápido.

 2
Author: Valentin Rusk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-27 16:16:01

Datetime COMO algo% tampoco captará el índice.

Use esto: DONDE datetime_field > = curdate();
Que atrapará el índice,
y cubrir hoy: 00: 00: 00 hasta hoy:23:59:59
Terminado.

 1
Author: Dr. Tyrell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-17 04:19:25

¿Qué dice 'explicar'? (run EXPLAIN SELECT * FROM transactionlist where date (TranDateTime) = '2008-08-17')

Si no está usando su índice debido a la función date (), una consulta de rango debería ejecutarse rápidamente:

SELECCIONE * DE LA lista DE transacciones donde TranDateTime > = ' 2008-08-17 'Y TranDateTime

 0
Author: nathan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-18 18:20:52

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

Donde TranDateTime > ' 2008-08-17 00:00:00' y TranDateTime

Esto permite que la base de datos use el índice en TranDateTime (hay uno, ¿verdad?) para hacer la selección.

 0
Author: Justsalt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-18 18:24:27

Crea nuevos campos con solo las fechas convert(datetime, left(date_field,10)) y luego indexa eso.

 0
Author: Mari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-15 03:19:34

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

Si utiliza magia funcional para * árboles, hashes, ... se ha ido, porque para obtener valores debe llamar a la función. Pero, debido a que usted no sabe los resultados por delante, usted tiene que hacer un análisis completo de la tabla.

No Hay nada que añadir.

Tal vez quieres decir algo como computado (calculado?) índices... 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 actualizado de clintp):

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

Si usas 00:00:00.0000 o 00:00 en mi opinión no hace ninguna diferencia (generalmente lo he usado en este formato).

 0
Author: antonia007,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-28 01:13:11

¿Por qué nadie sugirió usar LIKE? ¿No hace eso el trabajo también? ¿Será tan rápido como entre ambos?

SELECT * FROM transactionlist where TranDateTime LIKE '2008-08-17%'
 -2
Author: Nitro Chummy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-24 00:23:40