El índice de clave primaria con un DATETIME como primera parte de la clave compuesta nunca se usa

17

Tengo un problema con INDEXAR un DATETIME (o incluso una fecha) como primera parte de mi PRIMARY KEY.

Yo uso MySQL 5.5

Aquí están mis dos tablas:

-- This is my standard table with dateDim as a dateTime

CREATE TABLE `stats` (
 `dateDim` datetime NOT NULL,
 `accountDim` mediumint(8) unsigned NOT NULL,
 `execCodeDim` smallint(5) unsigned NOT NULL,
 `operationTypeDim` tinyint(3) unsigned NOT NULL,
 `junkDim` tinyint(3) unsigned NOT NULL,
 `ipCountryDim` smallint(5) unsigned NOT NULL,
 `count` int(10) unsigned NOT NULL,
 `amount` bigint(20) NOT NULL,
 PRIMARY KEY (`dateDim`,`accountDim`,`execCodeDim`,`operationTypeDim`,`junkDim`,`ipCountryDim`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8


-- Here is a copy with datDim as an integer

CREATE TABLE `stats_todays` (
`dateDim` int(11) unsigned NOT NULL,
 `accountDim` mediumint(8) unsigned NOT NULL,
 `execCodeDim` smallint(5) unsigned NOT NULL,
 `operationTypeDim` tinyint(3) unsigned NOT NULL,
 `junkDim` tinyint(3) unsigned NOT NULL,
 `ipCountryDim` smallint(5) unsigned NOT NULL,
 `count` int(10) unsigned NOT NULL,
 `amount` bigint(20) NOT NULL,
 PRIMARY KEY (`dateDim`,`accountDim`,`execCodeDim`,`operationTypeDim`,`junkDim`,`ipCountryDim`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

Completo ambas tablas con exactamente los mismos datos (cerca de 10 000 000)

Pero:

  • la tabla de estadísticas usa un DATETIME para dateDim
  • stats_todays usa un INTEGER con TO_DAYS () para dateDim

Mi pregunta es: ¿por qué MySQL NO UTILIZA la CLAVE PRIMARIA cuando la primera parte del índice es una fecha y hora? Es muy extraño ya que con los mismos datos pero consolidados con un INTEGER y TO_DAYS (dateDim) la misma solicitud oscila ...

Ejemplo con tabla de estadísticas (y fecha y hora):

SELECT * 
FROM `stats`  
WHERE 
   dateDim = '2014-04-03 00:00:00' 
   AND accountDim = 4
   AND execCodeDim = 9
   AND operationTypeDim = 1
   AND junkDim = 5
   AND ipCountryDim = 3

=> 1 result (4.5sec)

Explain:

id  select_type     table   type    possible_keys   key     key_len     ref     rows           Extra
1   SIMPLE          stats   ALL           NULL     NULL       NULL      NULL    8832329     Using where

Misma solicitud en la otra tabla stats_todays (con INTEGER y TO_DAYS ())

EXPLAIN SELECT * 
FROM `stats_todays`  
WHERE 
   dateDim = TO_DAYS('2014-04-03 00:00:00')
   AND accountDim = 4
   AND execCodeDim = 9
   AND operationTypeDim = 1
   AND junkDim = 5
   AND ipCountryDim = 3

=> Result 1 row (0.0003 sec) 

Explain:

id  select_type     table          type     possible_keys   key     key_len     ref                               rows  Extra
1   SIMPLE         stats_todays     const   PRIMARY     PRIMARY     13  const,const,const,const,const,const     1    

Si lee la publicación completa, comprende que no es un problema de baja cardinalidad ya que la solicitud funciona exactamente con la misma cardinalidad con un campo INTEGER dateDim ...

Aquí hay algunos detalles avanzados:

SELECT COUNT( DISTINCT dateDim )
FROM stats_todays
UNION ALL
SELECT COUNT( DISTINCT dateDim )
FROM stats;

Result:


COUNT(DISTINCT dateDim)
2192
2192

Aquí está la descripción del ÍNDICE:

SHOW INDEXES FROM `stats` 

Table   Non_unique  Key_name    Seq_in_index    Column_name     Collation   Cardinality     Sub_part    Packed  Null    Index_type  Comment     Index_comment
stats   0            PRIMARY          1         dateDim           A     6921           NULL                 NULL        BTREE        
stats   0            PRIMARY          2         accountDim        A     883232         NULL                 NULL        BTREE        
stats   0            PRIMARY          3         execCodeDim       A     8832329     NULL                NULL        BTREE        
stats   0            PRIMARY          4         operationTypeDim  A     8832329     NULL                NULL        BTREE        
stats   0            PRIMARY          5         junkDim           A     8832329     NULL                NULL        BTREE        
stats   0            PRIMARY          6         ipCountryDim      A     8832329     NULL                NULL        BTREE       

SHOW INDEXES FROM `stats_todays` 

Table   Non_unique  Key_name    Seq_in_index    Column_name     Collation   Cardinality     Sub_part    Packed  Null    Index_type  Comment     Index_comment
stats_todays    0   PRIMARY     1              dateDim              A        7518   NULL                   NULL         BTREE        
stats_todays    0   PRIMARY     2              accountDim           A        4022582    NULL                   NULL         BTREE        
stats_todays    0   PRIMARY     3              execCodeDim          A        8045164    NULL                   NULL         BTREE        
stats_todays    0   PRIMARY     4              operationTypeDim     A        8045164    NULL                   NULL         BTREE        
stats_todays    0   PRIMARY     5              junkDim              A        8045164    NULL                   NULL         BTREE        
stats_todays    0   PRIMARY     6              ipCountryDim         A        8045164    NULL                   NULL         BTREE        

SELECCIONE dateDim, COUNT (*) DESDE GRUPO de estadísticas POR dateDim CON ROLLUP

  • dice que hay 2192 fechas diferentes, y la distribución es suave (aproximadamente 3000 - 4000 filas por fecha)
  • hay 8 831 990 filas en la tabla
  • Lo mismo para la otra mesa.
  • Intenté con COVERING INDEX (reemplazando * por todas las columnas PK) => nada cambió
  • Intenté forzar | usar índice => nada cambió
  • Lo mismo con el campo de fecha en lugar de fecha y hora
  • Lo mismo con INDEX o UNIQUE en lugar de la clave primaria
nemenems
fuente
Esto es extraño de hecho. ¿Sucede lo mismo si usa en datelugar de datetime?
ypercubeᵀᴹ
sí, hace exactamente lo mismo
1
¿Y si corres WHERE dateDim = DATE('2014-04-03 00:00:00')?
ypercubeᵀᴹ
1
Con un nuevo pedido del pk funciona. Pero en realidad, quiero hacer una solicitud solo con dateDim y accountDim en la cláusula where. Uso todos los campos pk para el estudio de caso ...
1
WHERE dateDim = DATE ('2014-04-03 00:00:00') => nada ha cambiado

Respuestas:

6

Este es un error en 5.5.x. Ver aquí

Eso sugiere que su consulta debería ser

SELECT * 
FROM `stats`  
WHERE 
   dateDim = CAST('2014-04-03 00:00:00' as datetime)
   AND accountDim = 4
   AND execCodeDim = 9
   AND operationTypeDim = 1
   AND junkDim = 5
   AND ipCountryDim = 3
Ray Baxter
fuente
1

Desde la versión int de la tabla

CREATE TABLE `stats_todays` ( 
`dateDim` int(11) unsigned NOT NULL, 
 `accountDim` mediumint(8) unsigned NOT NULL, 
 `execCodeDim` smallint(5) unsigned NOT NULL, 
 `operationTypeDim` tinyint(3) unsigned NOT NULL, 
 `junkDim` tinyint(3) unsigned NOT NULL, 
 `ipCountryDim` smallint(5) unsigned NOT NULL, 
 `count` int(10) unsigned NOT NULL, 
 `amount` bigint(20) NOT NULL, 
 PRIMARY KEY (`dateDim`,`accountDim`,`execCodeDim`,`operationTypeDim`,`junkDim`,`ipCountryDim`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

funciona bien en términos de la consulta, debe tener dateDim que contenga UNIX_TIMESTAMP () de la cadena de fecha y hora. Su consulta se vería más así:

SELECT *        
FROM `stats`         
WHERE        
   dateDim = UNIX_TIMESTAMP('2014-04-03 00:00:00')
   AND accountDim = 4       
   AND execCodeDim = 9       
   AND operationTypeDim = 1       
   AND junkDim = 5       
   AND ipCountryDim = 3       
RolandoMySQLDBA
fuente