Related Documentation Download this Manual
PDF (US Ltr) - 40.8Mb
PDF (A4) - 40.9Mb
Man Pages (TGZ) - 294.0Kb
Man Pages (Zip) - 409.0Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb
Excerpts from this Manual

MySQL 8.3 Reference Manual  /  ...  /  LIST Partitioning

26.2.2 列表分区

MySQL 中的列表分区与范围分区类似,都是明确定义每个分区。两种分区类型的主要区别在于,列表分区是根据某一列值是否在某个值列表中来选择分区的,而不是根据某个连续的值范围。可以使用 PARTITION BY LIST(expr),其中 expr 是一个列值或基于列值的表达式,返回整数值,然后使用 VALUES IN (value_list) 定义每个分区,其中 value_list 是一个逗号分隔的整数列表。

Note

在 MySQL 8.3 中,可以在列表分区中匹配整数列表(可能还有 NULL,见 第 26.2.7 节,“MySQL 分区处理 NULL”)。

然而,在使用 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
);

(这与范围分区示例中使用的表相同。我们假设 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.3 中,可以使用 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 语句来恢复表的原始分区方案。)

与范围分区一样,也可以将列表分区与哈希或键分区结合使用,以生成复合分区(子分区)。见 第 26.2.6 节,“子分区”

与范围分区不同,列表分区没有类似 MAXVALUE 的“catch-all”;所有预期的分区表达式值都应该在 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 分区”