L'indice della chiave primaria con un DATETIME come prima parte della chiave composta non viene mai utilizzato


17

Ho un problema con INDICARE un DATETIME (o anche una data) come prima parte del mio PRIMARY KEY.

Uso MySQL 5.5

Ecco i miei due tavoli:

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

Riempo entrambe le tabelle con esattamente gli stessi dati (circa 10.000.000)

Ma:

  • la tabella delle statistiche usa DATETIME per dateDim
  • stats_todays usa un INTEGER con TO_DAYS () per dateDim

La mia domanda è: perché MySQL NON UTILIZZA LA CHIAVE PRIMARIA quando la prima parte dell'indice è un datetime ??? È molto strano poiché con gli stessi dati ma consolidati con un INTEGER e TO_DAYS (dateDim) la stessa richiesta oscilla ....

Esempio con tabella delle statistiche (e 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

Stessa richiesta sull'altra tabella stats_todays (Con INTEGER e 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    

Se leggi l'intero post, capisci che non si tratta di un problema di cardinalità bassa poiché la richiesta funziona esattamente con la stessa cardinalità con un campo INTEGER dateDim ....

Ecco alcuni dettagli avanzati:

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

Result:


COUNT(DISTINCT dateDim)
2192
2192

Ecco la descrizione dell'INDICE:

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        

SELEZIONA dateDim, COUNT (*) DA stats GROUP BY dateDim WITH ROLLUP

  • dice che ci sono 2192 date diverse e la ripartizione è regolare (circa 3000 - 4000 righe per data)
  • ci sono 8 831 990 righe nella tabella
  • Lo stesso per l'altro tavolo
  • Ho provato con COVERING INDEX (sostituendo * con tutte le colonne PK) => non è cambiato nulla
  • Ho provato a forzare | usa index => non è cambiato nulla
  • Lo stesso con il campo data invece datetime
  • Lo stesso con INDICE o UNICO invece della chiave primaria

Questo è davvero strano. Lo stesso succede se usi dateinvece di datetime?
ypercubeᵀᴹ

sì, fa esattamente lo stesso

1
E se corri WHERE dateDim = DATE('2014-04-03 00:00:00')?
ypercubeᵀᴹ

1
Con un riordino del pk funziona. Ma in realtà, voglio fare una richiesta con solo dateDim e accountDim nella clausola where. Uso tutti i campi pk per il case study ...

1
DOVE dateDim = DATE ('2014-04-03 00:00:00') => nulla è cambiato

Risposte:


6

Questo è un bug in 5.5.x. Vedi qui

Ciò suggerisce che la tua query dovrebbe essere

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

1

Dalla versione int della tabella

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 

funziona bene in termini di query, dovresti avere dateDim contenente UNIX_TIMESTAMP () della stringa datetime. La tua query sarebbe più simile a questa:

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       
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.