## 24.4 分区修剪

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

称为分区修剪的优化基于一个相对简单的概念，可以描述为“不要扫描没有匹配值的分区”。假设一个分区表`t1`由以下语句创建：

```
CREATE TABLE t1 (
    fname VARCHAR(50) NOT NULL,
    lname VARCHAR(50) NOT NULL,
    region_code TINYINT UNSIGNED NOT NULL,
    dob DATE NOT NULL
)
PARTITION BY RANGE( region_code ) (
    PARTITION p0 VALUES LESS THAN (64),
    PARTITION p1 VALUES LESS THAN (128),
    PARTITION p2 VALUES LESS THAN (192),
    PARTITION p3 VALUES LESS THAN MAXVALUE
);
```

假设您希望从一个[`选择`](select.html)像这样的声明：

```
SELECT fname, lname, region_code, dob
    FROM t1
    WHERE region_code > 125 AND region_code < 130;
```

很容易看出，应该返回的行都不在任何一个分区中`p0`要么`p3`;也就是说，我们只需要在分区中搜索`p1`和`p2`找到匹配的行。通过限制搜索，可以比扫描表中的所有分区花费更少的时间和精力来查找匹配的行。这种“切除”不需要的分区称为修剪。当优化器可以在执行此查询时使用分区修剪时，查询的执行速度可能比针对包含相同列定义和数据的非分区表的相同查询快一个数量级。

优化器可以在任何时候执行剪枝`在哪里`条件可以简化为以下两种情况之一：

-   `*`分区列`* = *`持续的`*`

-   `*`分区列`* 在 （*`常数1`*, *`常数2`*, ..., *`常数N`*)`

    在第一种情况下，优化器只计算给定值的分区表达式，确定哪个分区包含该值，然后只扫描这个分区。在许多情况下，等号可以替换为另一种算术比较，包括`<`,`>`,`<=`,`>=`， 和`<>`.一些查询使用`之间`在里面`在哪里`子句也可以利用分区修剪。请参阅本节后面的示例。

    在第二种情况下，优化器为列表中的每个值评估分区表达式，创建匹配分区列表，然后仅扫描此分区列表中的分区。

[`选择`](select.html),[`删除`](delete.html)， 和[`更新`](update.html)语句支持分区修剪。一个[`插入`](insert.html)语句也只访问每个插入行的一个分区；即使对于分区的表也是如此`哈希`要么`钥匙`虽然这目前没有显示在输出中[`解释`](explain.html).

修剪也可以应用于短距离，优化器可以将其转换为等效的值列表。例如，在前面的示例中，`在哪里`子句可以转换为`WHERE region_code IN (126, 127, 128, 129)`.然后优化器可以确定列表中的前两个值是在分区中找到的`p1`，partition中剩下的两个值`p2`，并且其他分区不包含相关值，因此不需要搜索匹配的行。

优化器还可以对`在哪里`涉及在使用表的多个列上比较上述类型的条件`范围列`要么`列出列`分区。

[](<>)

每当分区表达式由等式或可简化为一组等式的范围组成时，或者当分区表达式表示增加或减少的关系时，都可以应用这种类型的优化。修剪也可以应用于分区上的表[`日期`](datetime.html)要么[`约会时间`](datetime.html)当分区表达式使用[`年（）`](date-and-time-functions.html#function_year)要么[`TO_DAYS()`](date-and-time-functions.html#function_to-days)功能。当分区表达式使用[`TO_SECONDS()`](date-and-time-functions.html#function_to-seconds)功能。

假设那张桌子`t2`, 划分为[`日期`](datetime.html)列，是使用此处显示的语句创建的：

```
CREATE TABLE t2 (
    fname VARCHAR(50) NOT NULL,
    lname VARCHAR(50) NOT NULL,
    region_code TINYINT UNSIGNED NOT NULL,
    dob DATE NOT NULL
)
PARTITION BY RANGE( YEAR(dob) ) (
    PARTITION d0 VALUES LESS THAN (1970),
    PARTITION d1 VALUES LESS THAN (1975),
    PARTITION d2 VALUES LESS THAN (1980),
    PARTITION d3 VALUES LESS THAN (1985),
    PARTITION d4 VALUES LESS THAN (1990),
    PARTITION d5 VALUES LESS THAN (2000),
    PARTITION d6 VALUES LESS THAN (2005),
    PARTITION d7 VALUES LESS THAN MAXVALUE
);
```

以下语句使用`t2`可以利用分区修剪：

```
SELECT * FROM t2 WHERE dob = '1982-06-23';

UPDATE t2 SET region_code = 8 WHERE dob BETWEEN '1991-02-15' AND '1997-04-25';

DELETE FROM t2 WHERE dob >= '1984-06-21' AND dob <= '1999-06-21'
```

在最后一条语句的情况下，优化器还可以如下操作：

1.  *找到包含范围低端的分区*.

    [`年份('1984-06-21')`](date-and-time-functions.html#function_year)产生价值`1984年`，在分区中找到`d3`.

2.  *找到包含范围高端的分区*.

    [`年份('1999-06-21')`](date-and-time-functions.html#function_year)评估为`1999`，在分区中找到`d5`.

3.  *仅扫描这两个分区以及可能位于它们之间的任何分区*.

    在这种情况下，这意味着只有分区`d3`, `d4`， 和`d5`被扫描。剩余的分区可以被安全地忽略（并且被忽略）。

重要的

无效的`日期`和`约会时间`中引用的值`在哪里`针对分区表的语句的条件被视为`空值`.这意味着查询如`选择 * 从 *`分区表`* 在哪里 *`日期列`* < '2008-12-00'`不返回任何值（请参阅错误 #40972）。

到目前为止，我们只查看了使用示例`范围`分区，但修剪也可以应用于其他分区类型。

考虑一个被分区的表`列表`，其中分区表达式是递增或递减的，比如表`t3`显示在这里。（在这个例子中，为了简洁起见，我们假设`区域代码`列限于 1 到 10 之间的值。）

```
CREATE TABLE t3 (
    fname VARCHAR(50) NOT NULL,
    lname VARCHAR(50) NOT NULL,
    region_code TINYINT UNSIGNED NOT NULL,
    dob DATE NOT NULL
)
PARTITION BY LIST(region_code) (
    PARTITION r0 VALUES IN (1, 3),
    PARTITION r1 VALUES IN (2, 5, 8),
    PARTITION r2 VALUES IN (4, 9),
    PARTITION r3 VALUES IN (6, 7, 10)
);
```

对于诸如`SELECT * FROM t3 WHERE region_code BETWEEN 1 和 3`，优化器确定在哪些分区中找到值 1、2 和 3 (`r0`和`r1`) 并跳过其余的 (`r2`和`r3`）。

对于分区的表`哈希`要么`[线性]键`，分区修剪也是可能的，在这种情况下`在哪里`子句使用简单的`=`与分区表达式中使用的列的关系。考虑这样创建的表：

```
CREATE TABLE t4 (
    fname VARCHAR(50) NOT NULL,
    lname VARCHAR(50) NOT NULL,
    region_code TINYINT UNSIGNED NOT NULL,
    dob DATE NOT NULL
)
PARTITION BY KEY(region_code)
PARTITIONS 8;
```

可以修剪将列值与常量进行比较的语句：

```
UPDATE t4 WHERE region_code = 7;
```

修剪也可以用于短距离，因为优化器可以将这些条件变成`在`关系。例如，使用同一张表`t4`如前所述，可以修剪诸如此类的查询：

```
SELECT * FROM t4 WHERE region_code > 2 AND region_code < 6;

SELECT * FROM t4 WHERE region_code BETWEEN 3 AND 5;
```

在这两种情况下，`在哪里`子句由优化器转换为`WHERE region_code IN (3, 4, 5)`.

重要的

仅当范围大小小于分区数时才使用此优化。考虑这个陈述：

```
DELETE FROM t4 WHERE region_code BETWEEN 4 AND 12;
```

范围内`在哪里`子句涵盖 9 个值（4、5、6、7、8、9、10、11、12），但是`t4`只有8个分区。这意味着`删除`不能修剪。

当一个表被分区时`哈希`要么`[线性]键`，修剪只能用于整数列。例如，此语句不能使用剪枝，因为`出生日期`是一个[`日期`](datetime.html)柱子：

```
SELECT * FROM t4 WHERE dob >= '2001-04-14' AND dob <= '2005-10-15';
```

但是，如果表将年份值存储在[`INT`](integer-types.html)列，然后是一个查询`WHERE year_col >= 2001 AND year_col <= 2005`可以修剪。

使用提供自动分区的存储引擎的表，例如`新开发银行`如果 MySQL Cluster 使用的存储引擎被显式分区，则可以修剪它们。
