MySQL 8.4 Reference Manual  /  ...  /  LIST Partitioning

26.2.2 LIST 分区

MySQL 中的列表分区与范围分区在许多方面相似。正如在分区时使用 RANGE 类型一样,每个分区必须被明确定义。两种类型分区之间的主要差异是,列表分区中,每个分区都是基于一个列值所属的一个值列表集合来定义和选择,而不是基于连续范围内的值。在使用 PARTITION BY LIST(expr) 时,其中 expr 是一个列值或基于列值的表达式,并返回一个整数值,然后为每个分区定义使用 VALUES IN (value_列表),其中 value_列表 是一串用逗号分隔的整数值。

Note

在 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_engineInnoDB。)

假设有 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 分区”