### 24.3.1 RANGE 和 LIST 分区的管理

[](<>)[](<>)[](<>)[](<>)

范围和列表分区的添加和删除以类似的方式处理，因此我们将在本节中讨论这两种分区的管理。有关使用按散列或键分区的表的信息，请参阅[第 24.3.2 节，“HASH 和 KEY 分区的管理”](partitioning-management-hash-key.html).

从由任一分区的表中删除分区`范围`或通过`列表`可以使用[`更改表`](alter-table-partition-operations.html)与声明`删除分区`选项。假设您创建了一个按范围分区的表，然后使用以下命令填充了 10 条记录[`创建表`](create-table.html)和[`插入`](insert.html)声明：

```
mysql> CREATE TABLE tr (id INT, name VARCHAR(50), purchased DATE)
    ->     PARTITION BY RANGE( YEAR(purchased) ) (
    ->         PARTITION p0 VALUES LESS THAN (1990),
    ->         PARTITION p1 VALUES LESS THAN (1995),
    ->         PARTITION p2 VALUES LESS THAN (2000),
    ->         PARTITION p3 VALUES LESS THAN (2005),
    ->         PARTITION p4 VALUES LESS THAN (2010),
    ->         PARTITION p5 VALUES LESS THAN (2015)
    ->     );
Query OK, 0 rows affected (0.28 sec)

mysql> INSERT INTO tr VALUES
    ->     (1, 'desk organiser', '2003-10-15'),
    ->     (2, 'alarm clock', '1997-11-05'),
    ->     (3, 'chair', '2009-03-10'),
    ->     (4, 'bookcase', '1989-01-10'),
    ->     (5, 'exercise bike', '2014-05-09'),
    ->     (6, 'sofa', '1987-06-05'),
    ->     (7, 'espresso maker', '2011-11-22'),
    ->     (8, 'aquarium', '1992-08-04'),
    ->     (9, 'study desk', '2006-09-16'),
    ->     (10, 'lava lamp', '1998-12-25');
Query OK, 10 rows affected (0.05 sec)
Records: 10  Duplicates: 0  Warnings: 0
```

您可以看到哪些项目应该已插入分区`p2`如此处所示：

```
mysql> SELECT * FROM tr
    ->     WHERE purchased BETWEEN '1995-01-01' AND '1999-12-31';
+------+-------------+------------+
| id   | name        | purchased  |
+------+-------------+------------+
|    2 | alarm clock | 1997-11-05 |
|   10 | lava lamp   | 1998-12-25 |
+------+-------------+------------+
2 rows in set (0.00 sec)
```

您还可以使用分区选择来获取此信息，如下所示：

```
mysql> SELECT * FROM tr PARTITION (p2);
+------+-------------+------------+
| id   | name        | purchased  |
+------+-------------+------------+
|    2 | alarm clock | 1997-11-05 |
|   10 | lava lamp   | 1998-12-25 |
+------+-------------+------------+
2 rows in set (0.00 sec)
```

看[第 24.5 节，“分区选择”](partitioning-selection.html)， 了解更多信息。

删除名为的分区`p2`，执行以下命令：

```
mysql> ALTER TABLE tr DROP PARTITION p2;
Query OK, 0 rows affected (0.03 sec)
```

笔记

这[`NDBCLUSTER`](mysql-cluster.html)存储引擎不支持`ALTER TABLE ... 删除分区`.但是，它确实支持其他与分区相关的扩展[`更改表`](alter-table-partition-operations.html)本章所述。

记住这一点非常重要，*当你删除一个分区时，你也会删除该分区中存储的所有数据*.重新运行前面的可以看到是这样的[`选择`](select.html)询问：

```
mysql> SELECT * FROM tr WHERE purchased
    -> BETWEEN '1995-01-01' AND '1999-12-31';
Empty set (0.00 sec)
```

笔记

`删除分区`本机分区就地 API 支持，可与`算法={复制|插入}`.`删除分区`和`算法=就地`删除存储在分区中的数据并删除分区。然而，`删除分区`和`算法=复制`要么[`old_alter_table=开启`](server-system-variables.html#sysvar_old_alter_table)重建分区表并尝试将数据从删除的分区移动到另一个具有兼容的分区`分区...值`定义。无法移动到另一个分区的数据将被删除。

因此，您必须拥有[`降低`](privileges-provided.html#priv_drop)在执行之前获得表的权限`ALTER TABLE ... 删除分区`在那张桌子上。

如果您希望删除所有分区中的所有数据，同时保留表定义及其分区方案，请使用[`截断表`](truncate-table.html)陈述。（看[第 13.1.37 节，“TRUNCATE TABLE 语句”](truncate-table.html).)

如果您打算更改表的分区*没有*丢失数据，使用`ALTER TABLE ...重新组织分区`反而。见下文或[第 13.1.9 节，“ALTER TABLE 语句”](alter-table.html), 有关信息`重组分区`.

如果您现在执行[`显示创建表`](show-create-table.html)语句，你可以看到表的分区构成是如何改变的：

```
mysql> SHOW CREATE TABLE tr\G
*************************** 1. row ***************************
       Table: tr
Create Table: CREATE TABLE `tr` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(50) DEFAULT NULL,
  `purchased` date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY RANGE ( YEAR(purchased))
(PARTITION p0 VALUES LESS THAN (1990) ENGINE = InnoDB,
 PARTITION p1 VALUES LESS THAN (1995) ENGINE = InnoDB,
 PARTITION p3 VALUES LESS THAN (2005) ENGINE = InnoDB,
 PARTITION p4 VALUES LESS THAN (2010) ENGINE = InnoDB,
 PARTITION p5 VALUES LESS THAN (2015) ENGINE = InnoDB) */
1 row in set (0.00 sec)
```

当您将新行插入已更改的表中时`购买`列值之间`'1995-01-01'`和`'2004-12-31'`包括在内，这些行存储在分区中`p3`。您可以通过以下方式进行验证：

```
mysql> INSERT INTO tr VALUES (11, 'pencil holder', '1995-07-12');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM tr WHERE purchased
    -> BETWEEN '1995-01-01' AND '2004-12-31';
+------+----------------+------------+
| id   | name           | purchased  |
+------+----------------+------------+
|    1 | desk organiser | 2003-10-15 |
|   11 | pencil holder  | 1995-07-12 |
+------+----------------+------------+
2 rows in set (0.00 sec)

mysql> ALTER TABLE tr DROP PARTITION p3;
Query OK, 0 rows affected (0.03 sec)

mysql> SELECT * FROM tr WHERE purchased
    -> BETWEEN '1995-01-01' AND '2004-12-31';
Empty set (0.00 sec)
```

由于错误而从表中删除的行数`改变桌子。。。放置分区`服务器不会像对等服务器那样报告[`删去`](delete.html)查询

滴水`列表`分区使用完全相同的`改变桌子。。。放置分区`用于删除的语法`范围`分区。但是，这对以后使用表的影响有一个重要的区别：您不能再向表中插入任何具有定义已删除分区的值列表中包含的任何值的行。（见[第24.2.2节，“列表分区”](partitioning-list.html)，例如。）

要将新的范围或列表分区添加到以前分区的表中，请使用`改变桌子。。。添加分区`陈述对于按分区的表`范围`，这可用于将新范围添加到现有分区列表的末尾。假设您有一个包含组织成员数据的分区表，其定义如下：

```
CREATE TABLE members (
    id INT,
    fname VARCHAR(25),
    lname VARCHAR(25),
    dob DATE
)
PARTITION BY RANGE( YEAR(dob) ) (
    PARTITION p0 VALUES LESS THAN (1980),
    PARTITION p1 VALUES LESS THAN (1990),
    PARTITION p2 VALUES LESS THAN (2000)
);
```

进一步假设成员的最低年龄是16岁。随着2015年底的临近，你意识到你必须很快准备接纳2000年（及以后）出生的会员。您可以修改`成员`2000年至2010年出生的新成员人数表如下：

```
ALTER TABLE members ADD PARTITION (PARTITION p3 VALUES LESS THAN (2010));
```

对于按范围划分的表，可以使用`添加分区`只向分区列表的高端添加新分区。尝试以这种方式在现有分区之间或之前添加新分区会导致如下所示的错误：

```
mysql> ALTER TABLE members
     >     ADD PARTITION (
     >     PARTITION n VALUES LESS THAN (1970));
ERROR 1463 (HY000): VALUES LESS THAN value must be strictly »
   increasing for each partition
```

您可以通过将第一个分区重新组织为两个新分区来解决这个问题，将它们之间的范围分开，如下所示：

```
ALTER TABLE members
    REORGANIZE PARTITION p0 INTO (
        PARTITION n0 VALUES LESS THAN (1970),
        PARTITION n1 VALUES LESS THAN (1980)
);
```

使用[`显示创建表`](show-create-table.html)你可以看到`改变桌子`声明取得了预期效果：

```
mysql> SHOW CREATE TABLE members\G
*************************** 1. row ***************************
       Table: members
Create Table: CREATE TABLE `members` (
  `id` int(11) DEFAULT NULL,
  `fname` varchar(25) DEFAULT NULL,
  `lname` varchar(25) DEFAULT NULL,
  `dob` date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY RANGE ( YEAR(dob))
(PARTITION n0 VALUES LESS THAN (1970) ENGINE = InnoDB,
 PARTITION n1 VALUES LESS THAN (1980) ENGINE = InnoDB,
 PARTITION p1 VALUES LESS THAN (1990) ENGINE = InnoDB,
 PARTITION p2 VALUES LESS THAN (2000) ENGINE = InnoDB,
 PARTITION p3 VALUES LESS THAN (2010) ENGINE = InnoDB) */
1 row in set (0.00 sec)
```

另见[第13.1.9.1节，“更改表分区操作”](alter-table-partition-operations.html).

你也可以使用`改变桌子。。。添加分区`向按分区的表添加新分区的步骤`列表`1.假设有一张桌子`tt`使用以下公式定义[`创建表格`](create-table.html)声明：

```
CREATE TABLE tt (
    id INT,
    data INT
)
PARTITION BY LIST(data) (
    PARTITION p0 VALUES IN (5, 10, 15),
    PARTITION p1 VALUES IN (6, 12, 18)
);
```

您可以添加一个新分区，在其中存储具有`数据`列值`7.`, `14`和`21`如图所示：

```
ALTER TABLE tt ADD PARTITION (PARTITION p2 VALUES IN (7, 14, 21));
```

记住你*不能*添加一个新的`列表`包含已包含在现有分区的值列表中的任何值的分区。如果尝试执行此操作，将导致错误：

```
mysql> ALTER TABLE tt ADD PARTITION
     >     (PARTITION np VALUES IN (4, 8, 12));
ERROR 1465 (HY000): Multiple definition of same constant »
                    in list partitioning
```

因为任何与`数据`列值`12`已分配给分区`p1`，无法在表上创建新分区`tt`这包括`12`在它的值列表中。要做到这一点，你可以放弃`p1`，并添加`np`然后是一个新的`p1`修改了定义。但是，如前所述，这将导致存储在中的所有数据丢失`p1`-通常情况下，这并不是你真正想要做的。另一种解决方案似乎是使用新分区复制表，并使用[`创建表格。。。选择`](create-table.html)，然后删除旧表并重命名新表，但这在处理大量数据时可能非常耗时。在要求高可用性的情况下，这也可能不可行。

您可以在单个分区中添加多个分区`改变桌子。。。添加分区`声明如下：

```
CREATE TABLE employees (
  id INT NOT NULL,
  fname VARCHAR(50) NOT NULL,
  lname VARCHAR(50) NOT NULL,
  hired DATE NOT NULL
)
PARTITION BY RANGE( YEAR(hired) ) (
  PARTITION p1 VALUES LESS THAN (1991),
  PARTITION p2 VALUES LESS THAN (1996),
  PARTITION p3 VALUES LESS THAN (2001),
  PARTITION p4 VALUES LESS THAN (2005)
);

ALTER TABLE employees ADD PARTITION (
    PARTITION p5 VALUES LESS THAN (2010),
    PARTITION p6 VALUES LESS THAN MAXVALUE
);
```

幸运的是，MySQL的分区实现提供了在不丢失数据的情况下重新定义分区的方法。让我们先来看两个简单的例子，涉及`范围`分区。回想一下`成员`现在定义如下的表：

```
mysql> SHOW CREATE TABLE members\G
*************************** 1. row ***************************
       Table: members
Create Table: CREATE TABLE `members` (
  `id` int(11) DEFAULT NULL,
  `fname` varchar(25) DEFAULT NULL,
  `lname` varchar(25) DEFAULT NULL,
  `dob` date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY RANGE ( YEAR(dob))
(PARTITION n0 VALUES LESS THAN (1970) ENGINE = InnoDB,
 PARTITION n1 VALUES LESS THAN (1980) ENGINE = InnoDB,
 PARTITION p1 VALUES LESS THAN (1990) ENGINE = InnoDB,
 PARTITION p2 VALUES LESS THAN (2000) ENGINE = InnoDB,
 PARTITION p3 VALUES LESS THAN (2010) ENGINE = InnoDB) */
1 row in set (0.00 sec)
```

假设您希望将代表1960年之前出生的成员的所有行移动到一个单独的分区中。正如我们已经看到的，这不能通过使用[`改变桌子。。。添加分区`](alter-table-partition-operations.html)。但是，您可以使用另一个与分区相关的扩展来[`改变桌子`](alter-table-partition-operations.html)要做到这一点：

```
ALTER TABLE members REORGANIZE PARTITION n0 INTO (
    PARTITION s0 VALUES LESS THAN (1960),
    PARTITION s1 VALUES LESS THAN (1970)
);
```

实际上，这个命令会分割分区`p0`分为两个新分区`s0`和`s1`。它还会移动存储在中的数据`p0`根据这两个分区中体现的规则，将其转换为新分区`隔断价值观`条款，以便`s0`只包含[`年份（dob）`](date-and-time-functions.html#function_year)不到1960年`s1`包含以下行：[`年份（dob）`](date-and-time-functions.html#function_year)大于或等于1960年但小于1970年。

A.`重新组织分区`子句也可用于合并相邻分区。可以反转上一条语句对`成员`如下表所示：

```
ALTER TABLE members REORGANIZE PARTITION s0,s1 INTO (
    PARTITION p0 VALUES LESS THAN (1970)
);
```

使用拆分或合并分区时不会丢失任何数据`重新组织分区`.在执行上述语句时，MySQL会移动存储在分区中的所有记录`s0`和`s1`分割`p0`.

的一般语法`重新组织分区`如下所示：

```
ALTER TABLE tbl_name
    REORGANIZE PARTITION partition_list
    INTO (partition_definitions);
```

在这里*`tbl_名称`*已分区表的名称，以及*`分区列表`*是一个逗号分隔的列表，其中包含一个或多个要更改的现有分区的名称。*`分区定义`*是一个以逗号分隔的新分区定义列表，其遵循的规则与*`分区定义`*列表用于[`创建表格`](create-table.html)。使用时，您不仅限于将多个分区合并为一个分区，也不限于将一个分区拆分为多个分区`重新组织分区`。例如，您可以重新组织`成员`将表格一分为二，如下所示：

```
ALTER TABLE members REORGANIZE PARTITION p0,p1,p2,p3 INTO (
    PARTITION m0 VALUES LESS THAN (1980),
    PARTITION m1 VALUES LESS THAN (2000)
);
```

你也可以使用`重新组织分区`使用按分区的表`列表`.让我们回到在分区列表中添加新分区的问题`tt`由于新分区的值已存在于现有分区之一的值列表中，表中的值将被删除并失败。我们可以通过添加一个只包含非冲突值的分区来处理这个问题，然后重新组织新分区和现有分区，以便将存储在现有分区中的值移动到新分区：

```
ALTER TABLE tt ADD PARTITION (PARTITION np VALUES IN (4, 8));
ALTER TABLE tt REORGANIZE PARTITION p1,np INTO (
    PARTITION p1 VALUES IN (6, 18),
    PARTITION np VALUES in (4, 8, 12)
);
```

以下是使用时要记住的一些要点`改变桌子。。。重新组织分区`重新划分按分区的表的步骤`范围`或`列表`:

-   这个`隔断`用于确定新分区方案的选项与用于[`创建表格`](create-table.html)陈述

    一个新的`范围`分区方案不能有任何重叠范围；一个新的`列表`分区方案不能有任何重叠的值集。

-   中的分区组合*`分区定义`*列表中的值的范围或集合应与*`分区列表`*.

    例如，分区`p1`和`p2`加在一起涵盖1980年至1999年`成员`表在本节中用作示例。这两个分区的任何重组都应涵盖相同的总年限。

-   对于按分区的表`范围`，只能重新组织相邻分区；不能跳过范围分区。

    例如，您无法重新组织示例`成员`使用以开头的语句的表`ALTER表成员将分区p0、p2重组为。。。`因为`p0`涵盖1970年之前的年份，以及`p2`从1990年到1999年（含），所以这些不是相邻的分区。（不能跳过分区。）`p1`在这种情况下。）

-   你不能使用`重新组织分区`更改表使用的分区类型（例如，不能更改`范围`分区到`搞砸`分区（反之亦然）。也不能使用此语句更改分区表达式或列。要在不删除和重新创建表的情况下完成这些任务，可以使用[`改变桌子。。。被…分割。。。`](alter-table-partition-operations.html)，如图所示：

    ```
    ALTER TABLE members
        PARTITION BY HASH( YEAR(dob) )
        PARTITIONS 8;
    ```
