ไม่มีการใช้ดัชนีคีย์หลักที่มี DATETIME เนื่องจากส่วนแรกของคีย์ผสม


17

ฉันมีปัญหากับการทำดัชนีเวลา (หรือแม้กระทั่งวันที่) เป็นส่วนแรกของคีย์หลักของฉัน

ฉันใช้ MySQL 5.5

นี่คือสองตารางของฉัน:

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

ฉันเติมทั้งสองตารางด้วยข้อมูลเดียวกัน (เกือบ 10,000,000)

แต่:

  • ตารางสถิติใช้ DATETIME สำหรับ dateDim
  • stats_todays ใช้ un INTEGER กับ TO_DAYS () สำหรับ dateDim

คำถามของฉันคือ: ทำไม MySQL ไม่ใช้คีย์หลักเมื่อส่วนแรกของดัชนีคือวันที่และเวลา ??? มันแปลกมากเนื่องจากมีข้อมูลเดียวกัน แต่รวมเข้ากับ INTEGER และ TO_DAYS (dateDim) คำขอเดียวกันนี้จะทำให้หิน ....

ตัวอย่างที่มีตารางสถิติ (และวันที่และเวลา):

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

คำขอเดียวกันบน stats_todays ตารางอื่น ๆ (ด้วย INTEGER และ 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    

หากคุณอ่านโพสต์แบบเต็มคุณเข้าใจว่าไม่เป็นปัญหาเชิงการนับที่ต่ำเนื่องจากการร้องขอนั้นใช้งานได้กับ cardinality เดียวกับเขตข้อมูล INTEGER dateDim ...

นี่คือรายละเอียดขั้นสูง:

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

Result:


COUNT(DISTINCT dateDim)
2192
2192

นี่คือคำอธิบาย 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 (*) จากสถิติจัดกลุ่มตาม dateDim ด้วย ROLLUP

  • บอกว่ามีวันที่แตกต่างกัน 2192 วันและการแบ่งพาร์ติชั่นนั้นราบรื่น (ประมาณ 3,000 - 4000 แถวต่อวัน)
  • มี 8 831 990 แถวในตาราง
  • เช่นเดียวกันสำหรับตารางอื่น ๆ
  • ฉันลองด้วย COVERING INDEX (แทนที่ * ด้วยคอลัมน์ PK ทั้งหมด) => ไม่มีอะไรเปลี่ยนแปลง
  • ฉันลองใช้กำลัง | use index => ไม่มีอะไรเปลี่ยนแปลง
  • เขตข้อมูลเดียวกันกับวันที่แทน datetime
  • เช่นเดียวกันกับ INDEX หรือ UNIQUE แทนที่จะเป็นคีย์หลัก

นี่มันแปลกจริงๆ สิ่งเดียวกันนี้เกิดขึ้นหากคุณใช้dateแทนdatetimeหรือไม่?
ypercubeᵀᴹ

ใช่มันจะเหมือนกันทุก

1
และถ้าคุณทำงานWHERE dateDim = DATE('2014-04-03 00:00:00')?
ypercubeᵀᴹ

1
ด้วยการเรียงลำดับใหม่ของ pk มันทำงานได้ แต่ในความเป็นจริงแล้วฉันต้องการส่งคำขอเฉพาะ dateDim และ accountDim ในส่วนคำสั่ง where ฉันใช้ฟิลด์ pk ทั้งหมดสำหรับกรณีศึกษา ...

1
WHERE dateDim = DATE ('2014-04-03 00:00:00') => ไม่มีอะไรเปลี่ยนแปลง

คำตอบ:


6

นี่เป็นข้อบกพร่องใน 5.5.x ดูที่นี่

นั่นแสดงว่าแบบสอบถามของคุณควรเป็น

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

ตั้งแต่รุ่น int ของตาราง

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 

ทำงานได้ดีในแง่ของแบบสอบถามคุณควรมี dateDim มีUNIX_TIMESTAMP ()ของสตริง datetime คุณค้นหาจะมีลักษณะเช่นนี้:

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       
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.