MySQL 5.7 范围列分区

范围列分区
范围列分区类似于范围分区,但允许您使用基于多个列值的范围来定义分区。此外,还可以使用非整数类型的列来定义范围。

范围列分区与范围分区在以下几个方面有很大的不同:
.范围列分区不接受表达式,只接受列名。

.范围列分区接受一个或多个列的列表。
范围列分区基于元组(列值列表)之间的比较,而不是标量值之间的比较。在范围列分区中行的放置也基于元组之间的比较;本节后面将进一步 讨论这一点。

.范围列分区列不限制为整数列;string、DATE和DATETIME列也可以用作分区列。

创建由范围列分区的表的基本语法如下所示:

CREATE TABLE table_name
PARTITIONED BY RANGE COLUMNS(column_list) (
PARTITION partition_name VALUES LESS THAN (value_list)[,
PARTITION partition_name VALUES LESS THAN (value_list)][,
...]
)
column_list:
column_name[, column_name][, ...]
value_list:
value[, value][, ...]

在上述语法中,column_list是一个包含一个或多个列的列表(有时称为分区列列表),value_list是一个值列表(也就是说,它是一个分区定 义值列表)。每个分区定义都必须提供一个value_list,每个value_list的值数量必须与column_list的列数量相同。一般来说,如果在columns 子句中使用N列,那么每个VALUES小于子句也必须提供N个值的列表。

分区列列表中的元素以及定义每个分区的值列表中的元素必须按相同的顺序出现。此外,值列表中的每个元素必须与列列表中对应的元素具有相 同的数据类型。不过,分区列列表中的列名顺序以及值列表中的顺序不必与 CREATE TABLE 语句主部分中的表列定义顺序相同。与按范围分区的 表一样,您可以使用 MAXVALUE 来表示一个值,使得插入给定列中的任何合法值始终小于此值。以下是一个 CREATE TABLE 语句的示例,有助于 说明所有这些要点:

mysql> CREATE TABLE rcx (
    -> a INT,
    -> b INT,
    -> c CHAR(3),
    -> d INT
    -> )
    -> PARTITION BY RANGE COLUMNS(a,d,c) (
    -> PARTITION p0 VALUES LESS THAN (5,10,'ggg'),
    -> PARTITION p1 VALUES LESS THAN (10,20,'mmm'),
    -> PARTITION p2 VALUES LESS THAN (15,30,'sss'),
    -> PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE,MAXVALUE)
    -> );
Query OK, 0 rows affected (0.03 sec)

rcx表包含a、b、c、d列。提供给columns子句的分区列列表使用了其中3个列,按a、d、c的顺序。每个用于定义分区的值列表包含3个相同顺序 的值;也就是说,每个值列表元组的形式都是(INT, INT, CHAR(3)),对应于a、d、c列使用的数据类型(按此顺序)。

将行放置到分区中是通过比较将要插入的行的元组来确定的,该元组与COLUMNS子句中的列列表匹配,并用于定义表的分区的VALUES LESS THAN 子句。因为我们比较的是元组(即列表或值的集合)而不是标量值,所以范围列分区使用的values LESS than的语义与简单范围分区的情况有所 不同。在范围分区中,生成的表达式值等于VALUES小于中的一个极限值的行永远不会被放置到相应的分区中;然而,当使用范围列分区时,有时 可能会出现这样的情况:分区列列表的第一个元素的值与小于值列表的第一个元素的值相等,那么就被放置到相应的分区中。

考虑下面这个语句创建的范围分区表

mysql> CREATE TABLE r1 (
    -> a INT,
    -> b INT
    -> )
    -> PARTITION BY RANGE (a) (
    -> PARTITION p0 VALUES LESS THAN (5),
    -> PARTITION p1 VALUES LESS THAN (MAXVALUE)
    -> );
Query OK, 0 rows affected (0.02 sec)

如果我们在这个表中插入3行,使得a的每一行的列值为5,那么所有3行都存储在分区p1中,因为在每种情况下a列的值都不小于5,这可以通过对 INFORMATION_SCHEMA执行适当的查询来看到。分区表:

mysql> INSERT INTO r1 VALUES (5,10), (5,11), (5,12);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> SELECT PARTITION_NAME,TABLE_ROWS
    -> FROM INFORMATION_SCHEMA.PARTITIONS
    -> WHERE TABLE_NAME = 'r1';
+----------------+------------+
| PARTITION_NAME | TABLE_ROWS |
+----------------+------------+
| p0             |          0 |
| p1             |          3 |
+----------------+------------+
2 rows in set (0.00 sec)

现在考虑一个类似的表rc1,它使用范围列分区,在COLUMNS子句中同时引用a和b列,如下所示:

mysql> CREATE TABLE rc1 (
    -> a INT,
    -> b INT
    -> )
    -> PARTITION BY RANGE COLUMNS(a, b) (
    -> PARTITION p0 VALUES LESS THAN (5, 12),
    -> PARTITION p3 VALUES LESS THAN (MAXVALUE, MAXVALUE)
    -> );
Query OK, 0 rows affected (0.01 sec)

如果我们在rc1中插入与r1中完全相同的行,那么行的分布就会大不相同:

mysql> INSERT INTO rc1 VALUES (5,10), (5,11), (5,12);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> SELECT PARTITION_NAME,TABLE_ROWS
    -> FROM INFORMATION_SCHEMA.PARTITIONS
    -> WHERE TABLE_NAME = 'rc1';
+----------------+------------+
| PARTITION_NAME | TABLE_ROWS |
+----------------+------------+
| p0             |          2 |
| p3             |          1 |
+----------------+------------+
2 rows in set (0.00 sec)

这是因为我们比较的是行而不是标量值。我们可以将插入的行值与表rc1中用于定义分区p0的values THAN LESS THAN子句中的限制行值进行比较 ,如下所示:

mysql> SELECT (5,10) < (5,12), (5,11) < (5,12), (5,12) < (5,12);
+-----------------+-----------------+-----------------+
| (5,10) < (5,12) | (5,11) < (5,12) | (5,12) < (5,12) |
+-----------------+-----------------+-----------------+
|               1 |               1 |               0 |
+-----------------+-----------------+-----------------+
1 row in set (0.00 sec)

两个元组(5,10)和(5,11)的值小于(5,12),因此它们被存储在p0分区中。由于5不小于5,12不小于12,因此(5,12)被认为不小于(5,12 ),并存储在分区p1中。

上例中的SELECT语句也可以使用显式的行构造函数编写,如下所示:

mysql> SELECT ROW(5,10) < ROW(5,12), ROW(5,11) < ROW(5,12), ROW(5,12) < ROW(5,12);
+-----------------------+-----------------------+-----------------------+
| ROW(5,10) < ROW(5,12) | ROW(5,11) < ROW(5,12) | ROW(5,12) < ROW(5,12) |
+-----------------------+-----------------------+-----------------------+
|                     1 |                     1 |                     0 |
+-----------------------+-----------------------+-----------------------+
1 row in set (0.00 sec)

对于只使用单个分区列的按范围列分区的表,在分区中存储行与按范围分区的表是一样的。下面的CREATE TABLE语句使用1个分区列创建一个按 范围列分区的表:

mysql> CREATE TABLE rx (
    -> a INT,
    -> b INT
    -> )
    -> PARTITION BY RANGE COLUMNS (a) (
    -> PARTITION p0 VALUES LESS THAN (5),
    -> PARTITION p1 VALUES LESS THAN (MAXVALUE)
    -> );
Query OK, 0 rows affected (0.01 sec)

如果我们将(5,10),(5,11)和(5,12)行插入到这个表中,我们可以看到它们的位置与我们之前创建和填充的表r的位置相同:

mysql> INSERT INTO rx VALUES (5,10), (5,11), (5,12);
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> SELECT PARTITION_NAME,TABLE_ROWS
    -> FROM INFORMATION_SCHEMA.PARTITIONS
    -> WHERE TABLE_NAME = 'rx';
+----------------+------------+
| PARTITION_NAME | TABLE_ROWS |
+----------------+------------+
| p0             |          0 |
| p1             |          3 |
+----------------+------------+
2 rows in set (0.00 sec)

还可以创建按范围列进行分区的表,其中一个或多个列的限制值在连续的分区定义中重复出现。只要用于定义分区的列值元组严格递增,就可以 这样做。例如,下面的CREATE TABLE语句都是有效的。

mysql> CREATE TABLE rc2 (
    -> a INT,
    -> b INT
    -> )
    -> PARTITION BY RANGE COLUMNS(a,b) (
    -> PARTITION p0 VALUES LESS THAN (0,10),
    -> PARTITION p1 VALUES LESS THAN (10,20),
    -> PARTITION p2 VALUES LESS THAN (10,30),
    -> PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> CREATE TABLE rc3 (
    -> a INT,
    -> b INT
    -> )
    -> PARTITION BY RANGE COLUMNS(a,b) (
    -> PARTITION p0 VALUES LESS THAN (0,10),
    -> PARTITION p1 VALUES LESS THAN (10,20),
    -> PARTITION p2 VALUES LESS THAN (10,30),
    -> PARTITION p3 VALUES LESS THAN (10,35),
    -> PARTITION p4 VALUES LESS THAN (20,40),
    -> PARTITION p5 VALUES LESS THAN (MAXVALUE,MAXVALUE)
    -> );
Query OK, 0 rows affected (0.02 sec)

下面的语句也成功了,尽管乍一看可能不会成功,因为分区p0的b列的极限值是25,分区p1的极限值是20,分区p1的c列的极限值是100,分区p2 的极限值是50:

mysql> CREATE TABLE rc4 (
    -> a INT,
    -> b INT,
    -> c INT
    -> )
    -> PARTITION BY RANGE COLUMNS(a,b,c) (
    -> PARTITION p0 VALUES LESS THAN (0,25,50),
    -> PARTITION p1 VALUES LESS THAN (10,20,100),
    -> PARTITION p2 VALUES LESS THAN (10,30,50),
    -> PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE,MAXVALUE)
    -> );
Query OK, 0 rows affected (0.02 sec)

在设计按范围列进行分区的表时,你总是可以使用mysql客户端比较所需的元组来测试连续的分区定义,如下所示:

mysql> SELECT (0,25,50) < (10,20,100), (10,20,100) < (10,30,50);
+-------------------------+--------------------------+
| (0,25,50) < (10,20,100) | (10,20,100) < (10,30,50) |
+-------------------------+--------------------------+
|                       1 |                        1 |
+-------------------------+--------------------------+
1 row in set (0.00 sec)

如果CREATE TABLE语句包含的分区定义不是严格按递增顺序排列的,则会失败并报错,如下例所示:

mysql> CREATE TABLE rcf (
    -> a INT,
    -> b INT,
    -> c INT
    -> )
    -> PARTITION BY RANGE COLUMNS(a,b,c) (
    -> PARTITION p0 VALUES LESS THAN (0,25,50),
    -> PARTITION p1 VALUES LESS THAN (20,20,100),
    -> PARTITION p2 VALUES LESS THAN (10,30,50),
    -> PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE,MAXVALUE)
    -> );
ERROR 1493 (HY000): VALUES LESS THAN value must be strictly increasing for each partition

当出现这样的错误时,您可以通过在列列表之间进行“小于”比较来推断哪些分区定义是无效的。在这种情况下,问题出在分区p2的定义上,因 为用于定义它的元组不小于用于定义分区p3的元组,如下所示:

mysql> SELECT (0,25,50) < (20,20,100), (20,20,100) < (10,30,50);
+-------------------------+--------------------------+
| (0,25,50) < (20,20,100) | (20,20,100) < (10,30,50) |
+-------------------------+--------------------------+
|                       1 |                        0 |
+-------------------------+--------------------------+
1 row in set (0.00 sec)

在使用范围列时,同一列的MAXVALUE也可以出现在多个VALUES LESS THAN子句中。然而,在连续的分区定义中,单个列的限制值应该增加,不应 该有超过一个分区定义中有MAXVALUE,它用作所有列值的上限,该分区定义应该出现在分区列表的PARTITION ... VALUES LESS THAN子句的最后 。此外,您不能在多个分区定义中使用MAXVALUE作为第一列的限制值。

如前所述,范围列分区也可以使用非整数列作为分区列。考虑一个名为employees的表(未分区),使用以下语句创建:

CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT NOT NULL,
store_id INT NOT NULL
);

使用范围列分区,你可以创建这个表的一个版本,根据员工的姓氏将每一行存储在4个分区中的一个中,如下所示:

mysql> CREATE TABLE employees_by_lname (
    -> id INT NOT NULL,
    -> fname VARCHAR(30),
    -> lname VARCHAR(30),
    -> hired DATE NOT NULL DEFAULT '1970-01-01',
    -> separated DATE NOT NULL DEFAULT '9999-12-31',
    -> job_code INT NOT NULL,
    -> store_id INT NOT NULL
    -> )
    -> PARTITION BY RANGE COLUMNS (lname) (
    -> PARTITION p0 VALUES LESS THAN ('g'),
    -> PARTITION p1 VALUES LESS THAN ('m'),
    -> PARTITION p2 VALUES LESS THAN ('t'),
    -> PARTITION p3 VALUES LESS THAN (MAXVALUE)
    -> );
Query OK, 0 rows affected (0.02 sec)

或者,用户也可以通过执行下面的ALTER TABLE语句,对前面创建的employees表进行分区:

mysql> ALTER TABLE employees PARTITION BY RANGE COLUMNS (lname) (
    -> PARTITION p0 VALUES LESS THAN ('g'),
    -> PARTITION p1 VALUES LESS THAN ('m'),
    -> PARTITION p2 VALUES LESS THAN ('t'),
    -> PARTITION p3 VALUES LESS THAN (MAXVALUE)
    -> );
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

由于不同的字符集和排序规则具有不同的排序顺序,当使用字符串列作为分区列时,使用的字符集和排序规则可能会影响给定行的按范围列分区 的表的哪个分区。此外,在创建给定的数据库、表或列之后,更改其字符集或排序规则可能会导致行分布方式的更改。例如,当使用区分大小写 的排序规则时,` and `排在` Andersen `之前,但当使用不区分大小写的排序规则时,则相反。

类似地,用户可以使用下面的ALTER table语句对employees表进行分区,根据雇员的年龄,将表中的每一行存储在几个分区中的一个中:

mysql> ALTER TABLE employees PARTITION BY RANGE COLUMNS (hired) (
    -> PARTITION p0 VALUES LESS THAN ('1970-01-01'),
    -> PARTITION p1 VALUES LESS THAN ('1980-01-01'),
    -> PARTITION p2 VALUES LESS THAN ('1990-01-01'),
    -> PARTITION p3 VALUES LESS THAN ('2000-01-01'),
    -> PARTITION p4 VALUES LESS THAN ('2010-01-01'),
    -> PARTITION p5 VALUES LESS THAN (MAXVALUE)
    -> );
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0

MySQL 5.7 列表分区

列表分区
MySQL中的列表分区在很多方面与范围分区类似。与按范围分区一样,每个分区都必须显式定义。这两种分区的主要区别在于,在列表分区中, 每个分区是根据列值在一组值列表中的成员关系定义和选择的,而不是根据一组连续范围的值的成员关系。这是通过使用分区列表(expr)来完 成的,其中expr是一个列值或一个基于列值并返回整数值的表达式,然后通过(value_list)中的值来定义每个分区,其中value_list是一个逗 号分隔的整数值列表。与由range定义分区的情况不同,列表分区不需要以任何特定顺序声明。

对于下面的示例,我们假设要分区的表的基本定义由如下所示的CREATE table语句提供:

CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT,
store_id INT
);

要对表进行分区,使属于同一个区域的数据行存储在同一个分区中,用户可以使用下面的CREATE table语句:

mysql> CREATE TABLE employees (
    -> id INT NOT NULL,
    -> fname VARCHAR(30),
    -> lname VARCHAR(30),
    -> hired DATE NOT NULL DEFAULT '1970-01-01',
    -> separated DATE NOT NULL DEFAULT '9999-12-31',
    -> job_code INT,
    -> store_id INT
    -> )
    -> PARTITION BY LIST(store_id) (
    -> PARTITION pNorth VALUES IN (3,5,6,9,17),
    -> PARTITION pEast VALUES IN (1,2,10,11,19,20),
    -> PARTITION pWest VALUES IN (4,12,13,14,18),
    -> PARTITION pCentral VALUES IN (7,8,15,16)
    -> );
Query OK, 0 rows affected (0.02 sec)

这使得向表中添加或删除特定区域的员工记录变得容易。例如,假设西部地区的所有门店都卖给了另一家公司。在 MySQL 5.7 中,与该地区门 店员工相关的所有行都可以通过执行 ALTER TABLE employees TRUNCATE PARTITION pWest 这个查询来删除,这比执行等效的 DELETE 语句 DELETE FROM employees WHERE store_id IN (4,12,13,14,18); 要高效得多。(使用 ALTER TABLE employees DROP PARTITION pWest 也 会删除所有这些行,但还会从表的定义中移除分区 pWest;您需要使用 ALTER TABLE…ADD PARTITION 语句来恢复表的原始分区方案。)

与范围分区一样,也可以将列表分区与按哈希或键分区结合起来,以生成复合分区(子分区)。

与范围分区不同,这里没有诸如 MAXVALUE 这样的“万能”选项;分区表达式的所有预期值都应在 PARTITION … VALUES IN (…) 子句中涵 盖。包含未匹配分区列值的 INSERT 语句会因错误而失败,如下例所示:

mysql> CREATE TABLE h2 (
    ->   c1 INT,
    ->   c2 INT
    ->   )
    ->   PARTITION BY LIST(c1) (
    ->   PARTITION p0 VALUES IN (1, 4, 7),
    ->   PARTITION p1 VALUES IN (2, 5, 8)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO h2 VALUES (3, 5);
ERROR 1526 (HY000): Table has no partition for value 3

使用单个 INSERT 语句插入多行时,其行为取决于表是否使用事务存储引擎。对于 InnoDB 表,该语句被视为单个事务,因此任何不匹配的值的 存在都会导致整个语句完全失败,且不会插入任何行。对于使用非事务存储引擎(如 MyISAM)的表,包含不匹配值的行之前的任何行都会被插 入,但之后的行则不会。

使用IGNORE关键字可以忽略这种类型的错误。如果这样做,包含不匹配的分区列值的行不会被插入,但是包含匹配值的行会被插入,并且不会报告错误:

mysql> TRUNCATE h2;
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT * FROM h2;
Empty set (0.00 sec)

mysql> INSERT IGNORE INTO h2 VALUES (2, 5), (6, 10), (7, 5), (3, 1), (1, 9);
Query OK, 3 rows affected, 2 warnings (0.00 sec)
Records: 5  Duplicates: 2  Warnings: 2

mysql> SELECT * FROM h2;
+------+------+
| c1   | c2   |
+------+------+
|    7 |    5 |
|    1 |    9 |
|    2 |    5 |
+------+------+
3 rows in set (0.00 sec)

MySQL 5.7支持列表列分区。这是列表分区的一个变体,允许您使用整数类型以外的其他类型的列进行分区,也可以使用多个列作为分区键。

MySQL 5.7 范围分区

范围分区
按范围分区的表的分区方式是这样的:每个分区包含分区表达式值位于给定范围内的行。范围应该是连续的但不重叠的,并且使用VALUES LESS THAN操作符定义。在下面的几个例子中,假设您要创建一个表,如下面所示,用于保存20个音像店连锁店的人事记录,编号为1到20:

CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT NOT NULL,
store_id INT NOT NULL
);

这里使用的employees表没有主键或唯一键。虽然这些例子的效果与当前讨论的目的一致,但你应该记住,表在实践中很可能有主键、唯一键, 或者两者都有,如果存在分区列,那么是否允许分区列取决于用于这些键的列。

根据用户的需要,可以通过多种方式对该表进行分区。一种方法是使用store_id列。例如,你可能决定用4种方式对表进行分区,比如添加一个 partition by RANGE子句,如下所示:

mysql> CREATE TABLE employees (
    -> id INT NOT NULL,
    -> fname VARCHAR(30),
    -> lname VARCHAR(30),
    -> hired DATE NOT NULL DEFAULT '1970-01-01',
    -> separated DATE NOT NULL DEFAULT '9999-12-31',
    -> job_code INT NOT NULL,
    -> store_id INT NOT NULL
    -> )
    -> PARTITION BY RANGE (store_id) (
    -> PARTITION p0 VALUES LESS THAN (6),
    -> PARTITION p1 VALUES LESS THAN (11),
    -> PARTITION p2 VALUES LESS THAN (16),
    -> PARTITION p3 VALUES LESS THAN (21)
    -> );
Query OK, 0 rows affected (0.02 sec)

在这个分区方案中,在商店1 ~ 5工作的员工所对应的所有行存储在p0分区中,在商店6 ~ 10工作的员工所对应的行存储在p1分区中,以此类推 。注意,每个分区都是按从低到高的顺序定义的。这是分区范围语法的要求。你可以把它想象成C或Java中的一系列的if…elseif语句。

我们很容易就能确定要将包含数据(72,’Mitchell’, ‘Wilson’, ‘1998-06-25’, NULL, 13)的新行插入到分区p2中,但是当链增加了第21个存 储时会发生什么呢?在这种模式下,没有规则覆盖store_id大于20的行,因此会报错,因为服务器不知道把它放在哪里。为了避免这种情况,可 以在CREATE TABLE语句中使用包罗万象的VALUES LESS THAN子句,该子句提供所有大于显式指定的最大值的值:

mysql> CREATE TABLE employees (
    -> id INT NOT NULL,
    -> fname VARCHAR(30),
    -> lname VARCHAR(30),
    -> hired DATE NOT NULL DEFAULT '1970-01-01',
    -> separated DATE NOT NULL DEFAULT '9999-12-31',
    -> job_code INT NOT NULL,
    -> store_id INT NOT NULL
    -> )
    -> PARTITION BY RANGE (store_id) (
    -> PARTITION p0 VALUES LESS THAN (6),
    -> PARTITION p1 VALUES LESS THAN (11),
    -> PARTITION p2 VALUES LESS THAN (16),
    -> PARTITION p3 VALUES LESS THAN MAXVALUE
    -> );
Query OK, 0 rows affected (0.02 sec)

另一种避免在找不到匹配值时出错的方法是在INSERT语句中使用IGNORE关键字。

MAXVALUE表示一个整数值,总是大于可能的最大整数值(在数学语言中,它用作最小上界)。现在,任何store_id列值大于或等于16(定义的最 大值)的行都存储在分区p3中。在未来的某个时刻,当存储的数量增加到25、30或更多时,用户可以使用ALTER TABLE语句为存储21 ~ 25、26 ~ 30等添加新的分区(有关如何操作的细节,请参见22.3节)。

类似地,用户也可以根据员工的工作代码对表进行分区,也就是说,根据job_code列值的范围进行分区。例如,假设两位数的工作代码用于普通 (店内)员工,三位的工作代码用于办公室和技术支持人员,四位的工作代码用于管理职位,可以使用以下语句创建分区表:

mysql> CREATE TABLE employees (
    -> id INT NOT NULL,
    -> fname VARCHAR(30),
    -> lname VARCHAR(30),
    -> hired DATE NOT NULL DEFAULT '1970-01-01',
    -> separated DATE NOT NULL DEFAULT '9999-12-31',
    -> job_code INT NOT NULL,
    -> store_id INT NOT NULL
    -> )
    -> PARTITION BY RANGE (job_code) (
    -> PARTITION p0 VALUES LESS THAN (100),
    -> PARTITION p1 VALUES LESS THAN (1000),
    -> PARTITION p2 VALUES LESS THAN (10000)
    -> );
Query OK, 0 rows affected (0.01 sec)

在本例中,所有与店内员工相关的行都存储在p0分区中,与办公室和支持人员相关的行存储在p1分区中,与经理相关的行存储在p2分区中。

也可以在VALUES LESS THAN子句中使用表达式。然而,MySQL必须能够将表达式的返回值作为小于(< )比较的一部分进行计算。 您可以使用基于两个日期列之一的表达式,而不是根据存储编号拆分表数据。例如,假设你希望根据每个员工离开公司的年份进行划分;也就是 YEAR(separated)。下面是一个实现这种分区方案的CREATE TABLE语句的例子:

mysql> CREATE TABLE employees (
    -> id INT NOT NULL,
    -> fname VARCHAR(30),
    -> lname VARCHAR(30),
    -> hired DATE NOT NULL DEFAULT ‘1970-01-01’,
    -> separated DATE NOT NULL DEFAULT ‘9999-12-31’,
    -> job_code INT,
    -> store_id INT
    -> )
    -> PARTITION BY RANGE ( YEAR(separated) ) (
    -> PARTITION p0 VALUES LESS THAN (1991),
    -> PARTITION p1 VALUES LESS THAN (1996),
    -> PARTITION p2 VALUES LESS THAN (2001),
    -> PARTITION p3 VALUES LESS THAN MAXVALUE
    -> );
Query OK, 0 rows affected (0.01 sec)


在本方案中,对于1991年以前离职的所有员工,所有行都存储在p0分区中;对于1991年到1995年离开的人,p1;对于1996年到2000年离开的人, 在p2;对于2000年后离开的工人,也就是p3。

也可以根据时间戳列的值对表进行范围分区,使用UNIX_TIMESTAMP()函数,如下面的例子所示。
mysql> CREATE TABLE quarterly_report_status (
    -> report_id INT NOT NULL,
    -> report_status VARCHAR(20) NOT NULL,
    -> report_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
    -> )
    -> PARTITION BY RANGE ( UNIX_TIMESTAMP(report_updated) ) (
    -> PARTITION p0 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-01-01 00:00:00') ),
    -> PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-04-01 00:00:00') ),
    -> PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-07-01 00:00:00') ),
    -> PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-10-01 00:00:00') ),
    -> PARTITION p4 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-01-01 00:00:00') ),
    -> PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-04-01 00:00:00') ),
    -> PARTITION p6 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-07-01 00:00:00') ),
    -> PARTITION p7 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-10-01 00:00:00') ),
    -> PARTITION p8 VALUES LESS THAN ( UNIX_TIMESTAMP('2010-01-01 00:00:00') ),
    -> PARTITION p9 VALUES LESS THAN (MAXVALUE)
    -> );
Query OK, 0 rows affected (0.05 sec)

不允许使用任何其他涉及TIMESTAMP值的表达式。(参见Bug #42849。)

当满足以下一个或多个条件时,范围分区特别有用:
.你想要或者需要删除“旧”数据。如果用户使用前面给出的employees表分区方案,可以使用ALTER table employees drop partition p0语句 ;删除与1991年之前停止为公司工作的员工的所有行。对于包含很多行的表,这比执行DELETE查询(例如DELETE FROM employees WHERE YEAR (separated) < = 1990;)要高效。 .您希望使用包含日期或时间值的列,或者包含从其他系列产生的值。 .用户经常会执行直接依赖于用于分区表的列的查询。例如,当执行类似于EXPLAIN SELECT COUNT(*) FROM employees WHERE BETWEEN '2000- 01-01' AND '2000-12-31' GROUP BY store_id;的查询时,MySQL可以很快确定只有分区p2需要被扫描,因为剩余的分区不能包含任何满足 WHERE子句的记录。 这种分区的一种变体是范围列分区。根据范围列进行分区使得可以使用多个列来定义分区范围,这些范围既适用于分区中的行位置,也适用于在 执行分区修剪时确定包含或排除特定的分区。 基于时间间隔的分区方案。如果你想在MySQL 5.7中实现一个基于范围或时间间隔的分区方案,你有两个选择: 1.按范围对表进行分区,对于分区表达式,使用一个操作DATE、TIME或DATETIME列并返回整数值的函数,如下所示:

mysql> CREATE TABLE members (
    -> firstname VARCHAR(25) NOT NULL,
    -> lastname VARCHAR(25) NOT NULL,
    -> username VARCHAR(16) NOT NULL,
    -> email VARCHAR(35),
    -> joined DATE NOT NULL
    -> )
    -> PARTITION BY RANGE( YEAR(joined) ) (
    -> PARTITION p0 VALUES LESS THAN (1960),
    -> PARTITION p1 VALUES LESS THAN (1970),
    -> PARTITION p2 VALUES LESS THAN (1980),
    -> PARTITION p3 VALUES LESS THAN (1990),
    -> PARTITION p4 VALUES LESS THAN MAXVALUE
    -> );
Query OK, 0 rows affected (0.02 sec)

在MySQL 5.7中,也可以使用UNIX_TIMESTAMP()函数,根据时间戳列的值对表进行范围分区,如下面的例子所示:
mysql> CREATE TABLE quarterly_report_status (
    -> report_id INT NOT NULL,
    -> report_status VARCHAR(20) NOT NULL,
    -> report_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
    -> )
    -> PARTITION BY RANGE ( UNIX_TIMESTAMP(report_updated) ) (
    -> PARTITION p0 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-01-01 00:00:00') ),
    -> PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-04-01 00:00:00') ),
    -> PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-07-01 00:00:00') ),
    -> PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-10-01 00:00:00') ),
    -> PARTITION p4 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-01-01 00:00:00') ),
    -> PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-04-01 00:00:00') ),
    -> PARTITION p6 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-07-01 00:00:00') ),
    -> PARTITION p7 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-10-01 00:00:00') ),
    -> PARTITION p8 VALUES LESS THAN ( UNIX_TIMESTAMP('2010-01-01 00:00:00') ),
    -> PARTITION p9 VALUES LESS THAN (MAXVALUE)
    -> );
Query OK, 0 rows affected (0.05 sec)

在MySQL 5.7中,任何涉及时间戳值的表达式都是不允许的。(参见Bug #42849。)

在MySQL 5.7中也可以使用UNIX_TIMESTAMP(timestamp_column)作为按列表分区的表的分区表达式。然而,这样做通常是不实际的。

2.按范围列对表进行分区,使用DATE或DATETIME列作为分区列。例如,成员表可以直接使用合并的列定义,如下所示:

mysql> CREATE TABLE members (
    -> firstname VARCHAR(25) NOT NULL,
    -> lastname VARCHAR(25) NOT NULL,
    -> username VARCHAR(16) NOT NULL,
    -> email VARCHAR(35),
    -> joined DATE NOT NULL
    -> )
    -> PARTITION BY RANGE COLUMNS(joined) (
    -> PARTITION p0 VALUES LESS THAN ('1960-01-01'),
    -> PARTITION p1 VALUES LESS THAN ('1970-01-01'),
    -> PARTITION p2 VALUES LESS THAN ('1980-01-01'),
    -> PARTITION p3 VALUES LESS THAN ('1990-01-01'),
    -> PARTITION p4 VALUES LESS THAN MAXVALUE
    -> );
Query OK, 0 rows affected (0.02 sec)

使用除 DATE 或 DATETIME 之外的日期或时间类型的分区列,RANGE COLUMNS 不支持这种用法。