MySQL 9.0 参考手册  /  ...  /  范围分区

26.2.1 范围分区

按范围分区的表按以下方式分区:每个分区包含分区表达式值在给定范围内的行。范围应连续但不重叠,并使用 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 表没有主键或唯一键。虽然示例为了当前讨论的目的按所示工作,但您应该牢记在实践中表极有可能具有主键、唯一键或两者兼而有之,并且分区列的允许选择取决于用于这些键的列(如果有)。有关这些问题的讨论,请参见 第 26.6.1 节,“分区键、主键和唯一键”

此表可以按范围分区,方法很多,具体取决于您的需求。一种方法是使用 store_id 列。例如,您可以决定将表分区 4 次,方法是添加一个 PARTITION BY RANGE 子句,如下所示

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)
);

在这种分区方案中,与在商店 1 到 5 工作的员工相对应的所有行都存储在分区 p0 中,与在商店 6 到 10 工作的员工相对应的行存储在分区 p1 中,依此类推。每个分区按顺序定义,从最低到最高。这是 PARTITION BY RANGE 语法的要求;您可以认为它类似于 C 或 Java 中的一系列 if ... elseif ... 语句。

很容易确定包含数据 (72, 'Mitchell', 'Wilson', '1998-06-25', DEFAULT, 7, 13) 的新行将插入到分区 p2 中,但当您的连锁店增加第 21 家商店时会发生什么?在这种方案下,没有规则涵盖 store_id 大于 20 的行,因此会导致错误,因为服务器不知道将它放在哪里。您可以使用 CREATE TABLE 语句中的 catchall VALUES LESS THAN 子句来避免这种情况,该子句提供了所有大于明确命名的最高值的値

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
);

(与本章中的其他示例一样,我们假设默认存储引擎是 InnoDB。)

当未找到匹配的值时,避免发生错误的另一种方法是使用 INSERT 语句中的 IGNORE 关键字。有关示例,请参见 第 26.2.2 节,“列表分区”

MAXVALUE 表示一个始终大于最大可能整数值的整数值(在数学语言中,它用作 最小上界)。现在,所有 store_id 列值大于或等于 16(定义的最高值)的行都存储在分区 p3 中。在将来的某个时刻(当商店数量增加到 25、30 或更多时),您可以使用 ALTER TABLE 语句为商店 21-25、26-30 等添加新分区(有关如何执行此操作的详细信息,请参见 第 26.3 节,“分区管理”)。

以同样的方式,您可以根据员工职位代码对表进行分区,即根据 job_code 列值的范围。例如,假设使用两位数的职位代码来表示普通(店内)工人,使用三位数的代码来表示办公室和支持人员,使用四位数的代码来表示管理职位,那么您可以使用以下语句创建分区表

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)
);

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

也可以在 VALUES LESS THAN 子句中使用表达式。但是,MySQL 必须能够在执行 LESS THAN (<) 比较时计算表达式的返回值。

您可以使用基于两个 DATE 列之一的表达式来划分表数据,而不是根据商店编号进行划分。例如,假设您希望根据每个员工离开公司的年份进行分区,即 YEAR(separated) 的值。实现此类分区方案的 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 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
);

在这种方案中,所有在 1991 年之前离开的员工,其行都存储在分区 p0 中;在 1991 年到 1995 年之间离开的员工,其行存储在 p1 中;在 1996 年到 2000 年之间离开的员工,其行存储在 p2 中;对于任何在 2000 年之后离开的员工,其行存储在 p3 中。

也可以根据 TIMESTAMP 列的值按 RANGE 对表进行分区,使用 UNIX_TIMESTAMP() 函数,如以下示例所示

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)
);

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

当满足以下一个或多个条件时,范围分区特别有用

这种分区类型的一种变体是 RANGE COLUMNS 分区。通过 RANGE COLUMNS 分区,可以为定义分区范围使用多个列,这些范围同时适用于将行放置在分区中以及在执行分区剪枝时确定包含或排除特定分区。有关更多信息,请参见 第 26.2.3.1 节,“RANGE COLUMNS 分区”

基于时间间隔的分区方案。 如果您希望在 MySQL 9.0 中实现基于时间范围或间隔的分区方案,您有两种选择

  1. 通过 RANGE 对表进行分区,并将分区表达式用于对 DATETIMEDATETIME 列进行操作并返回整数值的函数,如下所示

    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
    );

    在 MySQL 9.0 中,还可以通过 RANGE 对表进行分区,基于 TIMESTAMP 列的值,使用 UNIX_TIMESTAMP() 函数,如以下示例所示

    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)
    );

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

    注意

    在 MySQL 9.0 中,还可以使用 UNIX_TIMESTAMP(timestamp_column) 作为按 LIST 分区的表的划分表达式。但是,这样做通常不切实际。

  2. 通过 RANGE COLUMNS 对表进行分区,使用 DATEDATETIME 列作为分区列。例如,members 表可以使用 joined 列直接定义,如下所示

    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
    );
注意

使用除 DATEDATETIME 以外的日期或时间类型作为分区列,在 RANGE COLUMNS 中不受支持。