Indeks kunci primer dengan DATETIME sebagai bagian pertama dari kunci majemuk tidak pernah digunakan

17

Saya memiliki masalah dengan INDEKS DATETIME (atau bahkan kencan) sebagai bagian pertama dari KUNCI UTAMA saya.

Saya menggunakan MySQL 5.5

Inilah dua meja saya:

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

Saya mengisi kedua tabel dengan data yang persis sama (mendekati 10.000)

Tapi:

  • tabel stats menggunakan DATETIME untuk dateDim
  • stats_todays gunakan un INTEGER dengan TO_DAYS () untuk dateDim

Pertanyaan saya adalah: mengapa MySQL TIDAK MENGGUNAKAN KUNCI UTAMA ketika bagian pertama dari indeks adalah sebuah datetime ??? Ini sangat aneh karena Dengan data yang sama tetapi dikonsolidasikan dengan INTEGER dan TO_DAYS (dateDim) permintaan yang sama mengguncang ....

Contoh dengan tabel statistik (dan 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

Permintaan yang sama pada tabel lainnya stats_todays (Dengan INTEGER dan 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    

Jika Anda membaca posting lengkap, Anda memahami bahwa itu bukan masalah kardinalitas rendah karena permintaan bekerja dengan kardinalitas yang sama persis dengan bidang INTEGER dateDim ....

Berikut ini beberapa detail lanjutan:

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

Result:


COUNT(DISTINCT dateDim)
2192
2192

Berikut ini adalah deskripsi 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 (*) DARI statistik GROUP BY dateDim WITH ROLLUP

  • memberitahu ada 2192 tanggal yang berbeda, dan partisi ulang itu lancar (sekitar 3000 - 4000 baris berdasarkan tanggal)
  • ada 8 831 990 baris dalam tabel
  • Sama untuk tabel lainnya
  • Saya mencoba dengan COVERING INDEX (mengganti * dengan semua kolom PK) => tidak ada yang berubah
  • Saya mencoba paksa | gunakan indeks => tidak ada yang berubah
  • Sama dengan bidang tanggal, bukan datetime
  • Sama dengan INDEX atau UNIQUE bukan kunci utama
nemenem
sumber
Ini memang aneh. Melakukan hal yang sama terjadi jika Anda menggunakan datebukan datetime?
ypercubeᵀᴹ
ya itu tidak persis sama
1
Dan jika Anda lari WHERE dateDim = DATE('2014-04-03 00:00:00')?
ypercubeᵀᴹ
1
Dengan menyusun ulang pk itu berfungsi. Tetapi pada kenyataannya, saya ingin membuat permintaan dengan hanya dateDim dan accountDim di mana klausa. Saya menggunakan semua bidang pk untuk studi kasus ...
1
WHERE dateDim = DATE ('2014-04-03 00:00:00') => tidak ada yang berubah

Jawaban:

6

Ini adalah bug di 5.5.x. Lihat di sini

Itu menunjukkan bahwa permintaan Anda seharusnya

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

Karena versi int tabel

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 

berfungsi dengan baik dalam hal permintaan, Anda harus memiliki dateDim berisi UNIX_TIMESTAMP () dari string datetime. Kueri Anda akan terlihat lebih seperti ini:

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
sumber