Der Primärschlüsselindex mit einem DATETIME als erstem Teil des zusammengesetzten Schlüssels wird niemals verwendet

17

Ich habe ein Problem mit der INDEXIERUNG einer DATETIME (oder sogar eines Datums) als ersten Teil meines PRIMARY KEY.

Ich benutze MySQL 5.5

Hier sind meine beiden Tabellen:

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

Ich fülle beide Tabellen mit genau den gleichen Daten (in der Nähe von 10 000 000)

Aber:

  • stats table benutze ein DATETIME für dateDim
  • stats_todays verwendet un INTEGER mit TO_DAYS () für dateDim

Meine Frage ist: Warum verwendet MySQL den PRIMARY KEY nicht, wenn der erste Teil des Indexes eine Datums- / Uhrzeitangabe ist? Es ist sehr seltsam, da mit den gleichen Daten, aber mit einem INTEGER und TO_DAYS (dateDim) die gleiche Anfrage rockt ....

Beispiel mit Statistiktabelle (und Datum / Uhrzeit):

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

Gleiche Anfrage auf der anderen Tabelle stats_todays (Mit INTEGER und 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    

Wenn Sie den vollständigen Beitrag lesen, verstehen Sie, dass dies kein Problem mit geringer Kardinalität ist, da die Anforderung mit genau derselben Kardinalität wie das Feld INTEGER dateDim ausgeführt wird.

Hier sind einige erweiterte Details:

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

Result:


COUNT(DISTINCT dateDim)
2192
2192

Hier ist die INDEX-Beschreibung:

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 Statistik GROUP BY dateDim WITH ROLLUP

  • gibt an, dass es 2192 verschiedene Daten gibt und die Aufteilung reibungslos ist (ca. 3000 - 4000 Zeilen nach Datum)
  • Die Tabelle enthält 8 831 990 Zeilen
  • Das gleiche gilt für den anderen Tisch
  • Ich habe versucht mit COVERING INDEX (* durch alle PK-Spalten ersetzen) => nichts geändert
  • Ich habe versucht, force | use index => nichts zu ändern
  • Dasselbe gilt für das Datumsfeld anstelle von datetime
  • Dasselbe gilt für INDEX oder UNIQUE anstelle des Primärschlüssels
nemenems
quelle
Das ist in der Tat seltsam. Passiert das auch, wenn Sie datestatt verwenden datetime?
ypercubeᵀᴹ
ja es macht genau das gleiche
1
Und wenn du rennst WHERE dateDim = DATE('2014-04-03 00:00:00')?
ypercubeᵀᴹ
1
Mit einer Nachbestellung des Pakets klappt es. In der Tat möchte ich eine Anfrage nur mit dateDim und accountDim in der where-Klausel stellen. Ich benutze alle pk-Felder für die Fallstudie ...
1
WHERE dateDim = DATE ('2014-04-03 00:00:00') => nichts geändert

Antworten:

6

Dies ist ein Fehler in 5.5.x. Sehen Sie hier

Das legt nahe, dass Ihre Abfrage sein sollte

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

Seit der int-Version der Tabelle

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 

Funktioniert die Abfrage einwandfrei, sollte dateDim den UNIX_TIMESTAMP () der datetime-Zeichenfolge enthalten. Ihre Anfrage würde ungefähr so ​​aussehen:

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
quelle