L'index de clé primaire avec un DATETIME comme première partie de la clé composée n'est jamais utilisé

17

J'ai un problème avec INDEXER une DATETIME (ou même une date) comme première partie de ma CLÉ PRIMAIRE.

J'utilise MySQL 5.5

Voici mes deux tableaux:

-- 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

Je remplis les deux tableaux avec exactement les mêmes données (près de 10 000 000)

Mais:

  • le tableau des statistiques utilise un DATETIME pour dateDim
  • stats_todays utilise un INTEGER avec TO_DAYS () pour dateDim

Ma question est: pourquoi MySQL n'utilise pas la clé primaire lorsque la première partie de l'index est un datetime ??? C'est très étrange puisque Avec les mêmes données mais consolidées avec un INTEGER et TO_DAYS (dateDim) la même requête bascule ....

Exemple avec table de statistiques (et datetime):

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

Même requête sur l'autre table stats_todays (Avec INTEGER et 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 vous lisez le post complet, vous comprenez que ce n'est pas un problème de faible cardinalité puisque la demande fonctionne avec exactement la même cardinalité avec un champ INTEGER dateDim ....

Voici quelques détails avancés:

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

Result:


COUNT(DISTINCT dateDim)
2192
2192

Voici la description INDEX:

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        

SELECT dateDim, COUNT (*) FROM stats GROUP BY dateDim WITH ROLLUP

  • indique qu'il y a 2192 dates différentes et que la répartition est fluide (environ 3000 à 4000 lignes par date)
  • il y a 8 831 990 lignes dans le tableau
  • Pareil pour l'autre table
  • J'ai essayé avec COVERING INDEX (en remplaçant * par toutes les colonnes PK) => rien n'a changé
  • J'ai essayé de forcer | use index => rien n'a changé
  • Le même avec le champ date au lieu de datetime
  • La même chose avec INDEX ou UNIQUE au lieu de la clé primaire
nemenems
la source
C'est vraiment étrange. La même chose se produit-elle si vous utilisez dateau lieu de datetime?
ypercubeᵀᴹ
oui, il fait exactement la même chose
1
Et si tu cours WHERE dateDim = DATE('2014-04-03 00:00:00')?
ypercubeᵀᴹ
1
Avec une réorganisation du pk, cela fonctionne. Mais dans les faits, je veux faire une demande avec seulement dateDim et accountDim dans la clause where. J'utilise tous les champs pk pour l'étude de cas ...
1
OERE dateDim = DATE ('2014-04-03 00:00:00') => rien n'a changé

Réponses:

6

Il s'agit d'un bogue dans 5.5.x. Voir ici

Cela suggère que votre requête doit être

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
la source
1

Depuis la version int de la table

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 

fonctionne bien en termes de requête, vous devriez avoir dateDim contenir l' UNIX_TIMESTAMP () de la chaîne datetime. Votre requête ressemblerait davantage à ceci:

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
la source