複合キーの最初の部分としてDATETIMEを持つ主キーインデックスは使用されません


17

PRIMARY KEYの最初の部分として、DATETIME(または日付)のインデックス付けに問題があります。

MySQL 5.5を使用します

これが私の2つのテーブルです。

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

両方のテーブルにまったく同じデータを入力します(100,000近く)

だが:

  • 統計表は、dateDimにDATETIMEを使用します
  • stats_todaysは、dateDimにTO_DAYS()でun INTEGERを使用します

私の質問は次のとおりです。なぜインデックスの最初の部分が日時であるときに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    

投稿全体を読むと、リクエストは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(*)FROM stats GROUP BY dateDim WITH ROLLUP

  • 2192の異なる日付があり、再パーティション化がスムーズであることを示しています(日付で約3000-4000行)
  • テーブルには8 831 990行あります
  • 他のテーブルでも同じ
  • COVERING INDEXで試しました(すべてのPK列で*を置き換えます)=>何も変更されませんでした
  • force | use index =>何も変更しませんでした
  • 日時ではなく日付フィールドでも同じ
  • 主キーの代わりにINDEXまたはUNIQUEでも同じ

これは確かに奇妙です。のdate代わりにを使用しても同じことが起こりますdatetimeか?
ypercubeᵀᴹ

はい、それは正確に同じことを

1
そして、あなたが走っWHERE dateDim = DATE('2014-04-03 00:00:00')たら?
ypercubeᵀᴹ

1
pkの並べ替えで機能します。しかし、実際には、where句にdateDimとaccountDimのみを使用してリクエストを作成します。私はケーススタディのためのすべてのPKフィールドを使用して...

1
dateDim = DATE( '2014年4月3日00:00:00')=>何も変わっていないWHERE

回答:


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にdatetime文字列のUNIX_TIMESTAMP()を含める必要があります。クエリは次のようになります。

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.