#### 8.2.1.13 条件过滤

在连接处理中，前缀行是从连接中的一个表传递到下一个表的那些行。通常，优化器会尝试将前缀计数较低的表放在连接顺序的早期，以防止行组合的数量快速增加。如果优化器可以使用有关从一个表中选择并传递给下一个表的行的条件信息，它可以更准确地计算行估计并选择最佳执行计划。

在没有条件过滤的情况下，表的前缀行数基于由表选择的估计行数`在哪里`子句根据优化器选择的任何访问方法。条件过滤使优化器能够在`在哪里`子句没有被访问方法考虑在内，从而改进了它的前缀行数估计。例如，即使可能有一种基于索引的访问方法可用于从连接中的当前表中选择行，但在连接中的表也可能存在附加条件。`在哪里`可以过滤（进一步限制）传递给下一个表的合格行的估计值的子句。

只有在以下情况下，条件才有助于过滤估计：

-   它指的是当前表。

-   它取决于连接序列中早期表中的一个或多个常量值。

-   访问方法尚未考虑到它。

    在[`解释`](explain.html)输出，`行`列表示所选访问方法的行估计，并且`过滤`列反映条件过滤的效果。`过滤`值以百分比表示。最大值为 100，这意味着没有过滤行。从 100 开始减小的值表示过滤量增加。

    前缀行数（估计从连接中的当前表传递到下一个表的行数）是`行`和`过滤`价值观。也就是说，前缀行数是估计的行数，减去估计的过滤效果。例如，如果`行`是 1000 并且`过滤`为 20%，条件过滤将估计的 1000 行数减少到 1000 × 20% = 1000 × .2 = 200 的前缀行数。

    考虑以下查询：


```
SELECT *
  FROM employee JOIN department ON employee.dept_no = department.dept_no
  WHERE employee.first_name = 'John'
  AND employee.hire_date BETWEEN '2018-01-01' AND '2018-06-01';
```

假设数据集具有以下特征：

-   这`员工`表有 1024 行。

-   这`部`表有 12 行。

-   两个表都有一个索引`部门号`.

-   这`员工`表上有一个索引`名`.

-   8行满足这个条件`雇员.first_name`：

    ```
    employee.first_name = 'John'
    ```

-   150 行满足这个条件`雇员.hire_date`：

    ```
    employee.hire_date BETWEEN '2018-01-01' AND '2018-06-01'
    ```

-   1 行满足两个条件：

    ```
    employee.first_name = 'John'
    AND employee.hire_date BETWEEN '2018-01-01' AND '2018-06-01'
    ```

    没有条件过滤，[`解释`](explain.html)产生这样的输出：


```
+----+------------+--------+------------------+---------+---------+------+----------+
| id | table      | type   | possible_keys    | key     | ref     | rows | filtered |
+----+------------+--------+------------------+---------+---------+------+----------+
| 1  | employee   | ref    | name,h_date,dept | name    | const   | 8    | 100.00   |
| 1  | department | eq_ref | PRIMARY          | PRIMARY | dept_no | 1    | 100.00   |
+----+------------+--------+------------------+---------+---------+------+----------+
```

为了`员工`, 上的访问方法`姓名`索引选取与名称匹配的 8 行`'约翰'`.没有过滤（`过滤`是 100%），所以所有行都是下一个表的前缀行：前缀行数是`行`×`过滤`= 8 × 100% = 8。

通过条件过滤，优化器还考虑了来自`在哪里`访问方法未考虑子句。在这种情况下，优化器使用启发式方法估计过滤效果为 16.31%[`之间`](comparison-operators.html#operator_between)条件`雇员.hire_date`.因此，[`解释`](explain.html)产生这样的输出：

```
+----+------------+--------+------------------+---------+---------+------+----------+
| id | table      | type   | possible_keys    | key     | ref     | rows | filtered |
+----+------------+--------+------------------+---------+---------+------+----------+
| 1  | employee   | ref    | name,h_date,dept | name    | const   | 8    | 16.31    |
| 1  | department | eq_ref | PRIMARY          | PRIMARY | dept_no | 1    | 100.00   |
+----+------------+--------+------------------+---------+---------+------+----------+
```

现在前缀行数是`行`×`过滤`= 8 × 16.31% = 1.3，更接近真实数据集。

通常，优化器不会为最后一个连接的表计算条件过滤效果（前缀行数减少），因为没有下一个表可以传递行。发生异常[`解释`](explain.html)：为了提供更多信息，过滤效果是针对所有连接表计算的，包括最后一个。

要控制优化器是否考虑其他过滤条件，请使用[`条件扇出过滤器`](switchable-optimizations.html#optflag_condition-fanout-filter)国旗[`优化器开关`](server-system-variables.html#sysvar_optimizer_switch)系统变量（见[第 8.9.2 节，“可切换的优化”](switchable-optimizations.html)）。默认情况下启用此标志，但可以禁用以抑制条件过滤（例如，如果发现特定查询在没有它的情况下会产生更好的性能）。

如果优化器高估了条件过滤的效果，性能可能会比不使用条件过滤的情况更差。在这种情况下，这些技术可能会有所帮助：

-   如果一个列没有被索引，那么索引它以便优化器有一些关于列值分布的信息并且可以改进它的行估计。

-   同样，如果没有可用的列直方图信息，则生成直方图（请参阅[第 8.9.6 节，“优化器统计”](optimizer-statistics.html)）。

-   更改连接顺序。实现这一点的方法包括连接顺序优化器提示（请参阅[第 8.9.3 节，“优化器提示”](optimizer-hints.html)),`STRAIGHT_JOIN`紧随其后`选择`, 和`STRAIGHT_JOIN`加入运算符。

-   禁用会话条件过滤：

    ```
    SET optimizer_switch = 'condition_fanout_filter=off';
    ```

    或者，对于给定的查询，使用优化器提示：

    ```
    SELECT /*+ SET_VAR(optimizer_switch = 'condition_fanout_filter=off') */ ...
    ```
