如何按日期时间列对表进行分区?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何按日期时间列对表进行分区?相关的知识,希望对你有一定的参考价值。

我想通过datetime列对mysql表进行分区。有一天分区。创建表脚本是这样的:

CREATE TABLE raw_log_2011_4 (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  logid char(16) NOT NULL,
  tid char(16) NOT NULL,
  reporterip char(46) DEFAULT NULL,
  ftime datetime DEFAULT NULL,
  KEY id (id)
) ENGINE=InnoDB AUTO_INCREMENT=286802795 DEFAULT CHARSET=utf8
PARTITION BY hash (day(ftime)) partitions 31;

但是当我选择某天的数据时,它找不到分区.select语句是这样的:

explain partitions select * from raw_log_2011_4 where day(ftime) = 30;

当我使用另一个语句时,它可以找到分区,但我无法选择某一天的数据。

explain partitions select * from raw_log_2011_4 where ftime = '2011-03-30';

有没有人告诉我如何选择某天的数据并利用分区。谢谢!

答案

HASH的分区对于datetime列是个坏主意,因为它不能使用partition pruning。来自MySQL文档:

修剪只能用于由HASH或KEY分区的表的整数列。例如,表t4上的此查询无法使用修剪,因为dob是DATE列:

SELECT * FROM t4 WHERE dob >= '2001-04-14' AND dob <= '2005-10-15';

但是,如果表将年份值存储在INT列中,则可以修剪具有WHERE year_col> = 2001 AND year_col <= 2005的查询。

因此,您可以将TO_DAYS(DATE())的值存储在额外的INTEGER列中以使用修剪。

另一种选择是使用RANGE分区:

CREATE TABLE raw_log_2011_4 (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  logid char(16) NOT NULL,
  tid char(16) NOT NULL,
  reporterip char(46) DEFAULT NULL,
  ftime datetime DEFAULT NULL,
  KEY id (id)
) ENGINE=InnoDB AUTO_INCREMENT=286802795 DEFAULT CHARSET=utf8
  PARTITION BY RANGE( TO_DAYS(ftime) ) (
    PARTITION p20110401 VALUES LESS THAN (TO_DAYS('2011-04-02')),
    PARTITION p20110402 VALUES LESS THAN (TO_DAYS('2011-04-03')),
    PARTITION p20110403 VALUES LESS THAN (TO_DAYS('2011-04-04')),
    PARTITION p20110404 VALUES LESS THAN (TO_DAYS('2011-04-05')),
    ...
    PARTITION p20110426 VALUES LESS THAN (TO_DAYS('2011-04-27')),
    PARTITION p20110427 VALUES LESS THAN (TO_DAYS('2011-04-28')),
    PARTITION p20110428 VALUES LESS THAN (TO_DAYS('2011-04-29')),
    PARTITION p20110429 VALUES LESS THAN (TO_DAYS('2011-04-30')),
    PARTITION future VALUES LESS THAN MAXVALUE
  );

现在,以下查询将仅使用分区p20110403:

SELECT * FROM raw_log_2011_4 WHERE ftime = '2011-04-03';
另一答案

嗨您正在对表定义所希望的表的定义中执行错误的分区:

CREATE TABLE raw_log_2011_4 (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  logid char(16) NOT NULL,
  tid char(16) NOT NULL,
  reporterip char(46) DEFAULT NULL,
  ftime datetime DEFAULT NULL,
  KEY id (id)
) ENGINE=InnoDB AUTO_INCREMENT=286802795 DEFAULT CHARSET=utf8
PARTITION BY hash (TO_DAYS(ftime)) partitions 31;

你的选择命令是:

explain partitions 
    select * from raw_log_2011_4 where TO_DAYS(ftime) = '2011-03-30';

上面的命令将选择所需的所有日期,就像使用TO_DAYS命令一样

mysql> SELECT TO_DAYS(950501);
        -> 728779
mysql> SELECT TO_DAYS('2007-10-07');
        -> 733321

为什么要使用TO_DAYS AS MySQL优化器将识别两个基于日期的功能以进行分区修剪:1.TO_DAYS()2.YEAR()

这将解决你的问题..

另一答案

我刚刚在http://dev.mysql.com/tech-resources/articles/mysql_55_partitioning.html上阅读了一篇与此相关的MySQL博客文章。

早于5.1的版本需要特殊的体操才能根据日期进行分区。上面的链接讨论它并显示示例。

版本5.5及更高版本允许您使用非数字值(如日期和字符串)进行直接分区。

另一答案

不要使用CHAR,使用VARCHAR。这将节省大量空间,从而减少I / O,从而加快查询速度。

reporterip:(46)对于IP地址来说是不必要的,甚至是IPv6。有关进一步的讨论,请参阅My blog,包括如何将其缩小到16个字节。

PARTITION BY RANGE(TO_DAYS(...))为@Steyx建议,但没有超过50个分区。尽管进行了“修剪”,但您拥有的分区越多,查询就越慢。 HASH分区基本没用。

More discussion of partitioning, especially the type you are looking at。这包括随着时间的推移滑动分区集的代码。

以上是关于如何按日期时间列对表进行分区?的主要内容,如果未能解决你的问题,请参考以下文章

按 2 列对表进行分组并优先考虑非空值?

我想按姓氏对表进行分区? [关闭]

按字符和日期列对数据框进行排序

使用javascript或jquery根据表头列对表行进行排序

按两列对 MySQL 表排序

html 使用flexbox和一些Javascript按列对表进行排序