26.2.2 LIST 分区
MySQL 中的列表分区与范围分区在许多方面相似。正如在分区时使用 RANGE
类型一样,每个分区必须被明确定义。两种类型分区之间的主要差异是,列表分区中,每个分区都是基于一个列值所属的一个值列表集合来定义和选择,而不是基于连续范围内的值。在使用 PARTITION BY LIST(
时,其中 expr
)expr
是一个列值或基于列值的表达式,并返回一个整数值,然后为每个分区定义使用 VALUES IN (
,其中 value_列表
)value_列表
是一串用逗号分隔的整数值。
在 MySQL 8.4 中,可以仅匹配一个整数列表(并且可能是 NULL
—参见第 26.2.7 节,“MySQL 分区如何处理 NULL”) 当使用 LIST
时进行分区。
然而,当使用 LIST COLUMN
分区时,其他列类型可能在值列表中被使用,这将在本节后面描述。
与范围分区定义的分区不同,列表分区不需要以任何特定顺序声明。对于更详细的语法信息,请参见第 15.1.20 节,“CREATE TABLE 语句”。
以下示例中,我们假设要分区的表的基本定义由以下 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
);
(这与其他分区示例中使用的表相同,参见第 26.2.1 节,“范围分区”。与其他分区示例一样,我们假设 default_storage_engine
是 InnoDB
。)
假设有 20 家视频店分散在 4 个连锁店中,如下表所示。
Region | Store ID Numbers |
---|---|
北方 | 3, 5, 6, 9, 17 |
东方 | 1, 2, 10, 11, 19, 20 |
西方 | 4, 12, 13, 14, 18 |
中央 | 7, 8, 15, 16 |
要将表按区域划分,以便同一区域的店铺行存储在相同的分区中,您可以使用以下 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
)
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)
);
这使得添加或删除特定区域员工记录到或从表中变得容易。例如,假设西方地区的所有店铺被另一家公司购买。在 MySQL 8.4 中,可以使用查询 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
语句来恢复表的原始分区方案。
与 RANGE
分区相似,列表分区可以与哈希或键分区结合使用,以产生一个复合分区(子分区)。参见第 26.2.6 节,“子分区”。
与 RANGE
分区不同,没有一个称为““catch-all””的东西,如 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.11 sec)
mysql> INSERT INTO h2 VALUES (3, 5);
ERROR 1525 (HY000): Table has no partition for value 3
当使用单个 INSERT
语句将多行插入到单个 InnoDB
表中时,InnoDB
将该语句视为单个事务,因此任何未匹配值的存在都会导致整个语句失败,并且因此不会插入任何行。
您可以通过使用 IGNORE
关键字来忽略这种类型的错误,尽管每一行包含不匹配的分区列值时都会发出警告,如下所示。
mysql> TRUNCATE h2;
Query OK, 1 row affected (0.00 sec)
mysql> TABLE 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.01 sec)
Records: 5 Duplicates: 2 Warnings: 2
mysql> SHOW WARNINGS;
+---------+------+------------------------------------+
| Level | Code | Message |
+---------+------+------------------------------------+
| Warning | 1526 | Table has no partition for value 6 |
| Warning | 1526 | Table has no partition for value 3 |
+---------+------+------------------------------------+
2 rows in set (0.00 sec)
在以下 TABLE
语句的输出中,您可以看到包含不匹配分区列值的行被静默拒绝,而包含无不匹配值的行则被插入到表中:
mysql> TABLE h2;
+------+------+
| c1 | c2 |
+------+------+
| 7 | 5 |
| 1 | 9 |
| 2 | 5 |
+------+------+
3 rows in set (0.00 sec)
MySQL 还提供了 LIST COLUMNS
分区支持,这是一种 LIST
分区的变体,允许您使用除整数外的列类型作为分区列,并且可以使用多个列作为分区键。有关更多信息,请参阅 第 26.2.3.2 节,“LIST COLUMNS 分区”。