복합 키의 첫 부분으로 DATETIME을 갖는 기본 키 인덱스는 사용되지 않습니다


17

PRIMARY KEY의 첫 번째 부분으로 DATETIME (또는 날짜)을 INDEXING하는 데 문제가 있습니다.

나는 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 근처)

그러나:

  • 통계표는 dateDim에 DATETIME을 사용합니다.
  • stats_todays는 dateDim에 대해 TO_DAYS ()와 함께 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        

SELECTDim, COUNT (*) 통계에서 GROUP BY dateDim WITH ROLLUP

  • 2192 개의 서로 다른 날짜가 있고 재분할이 매끄럽다 고 알려줍니다 (날짜별로 약 3000-4000 행)
  • 테이블에 8 831 990 개의 행이 있습니다
  • 다른 테이블과 동일
  • COVERING INDEX (모든 PK 열에 의해 * 대체) => 아무것도 변경되지 않았습니다.
  • 나는 force | use index => 아무것도 변경하지 않았다.
  • datetime 대신 date 필드와 동일
  • 기본 키 대신 INDEX 또는 UNIQUE와 동일

실제로 이상하다. date대신에 사용하면 같은 일이 발생합니까 datetime?
ypercubeᵀᴹ

네, 정확히 같은 일이

1
그리고 당신이 달리면 WHERE dateDim = DATE('2014-04-03 00:00:00')?
ypercubeᵀᴹ

1
pk를 재정렬하면 작동합니다. 그러나 실제로 where 절에서 dateDim 및 accountDim 만 사용하여 요청하고 싶습니다. 사례 연구에 모든 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.