#### 8.2.1.20 函数调用优化

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

MySQL 函数在内部被标记为确定性或非确定性。一个函数是不确定的，如果给定其参数的固定值，它可以为不同的调用返回不同的结果。非确定性函数的示例：[`兰德（）`](mathematical-functions.html#function_rand),[`UUID()`](miscellaneous-functions.html#function_uuid).

如果函数被标记为非确定性，则在`在哪里`为每一行（从一个表中选择时）或行组合（从多表连接中选择时）评估子句。

MySQL 还根据参数类型确定何时评估函数，参数是表列还是常量值。每当该列更改值时，必须评估将表列作为参数的确定性函数。

非确定性函数可能会影响查询性能。例如，某些优化可能不可用，或者可能需要更多锁定。以下讨论使用[`兰德（）`](mathematical-functions.html#function_rand)但也适用于其他非确定性函数。

假设一个表`吨`有这个定义：

```
CREATE TABLE t (id INT NOT NULL PRIMARY KEY, col_a VARCHAR(100));
```

考虑以下两个查询：

```
SELECT * FROM t WHERE id = POW(1,2);
SELECT * FROM t WHERE id = FLOOR(1 + RAND() * 49);
```

由于与主键的相等性比较，这两个查询似乎都使用主键查找，但这仅适用于它们中的第一个：

-   第一个查询总是最多产生一行，因为[`战俘（）`](mathematical-functions.html#function_pow)with constant arguments 是一个常量值，用于索引查找。

-   第二个查询包含一个使用非确定性函数的表达式[`兰德（）`](mathematical-functions.html#function_rand)，它在查询中不是常量，但实际上对表的每一行都有一个新值`吨`.因此，查询读取表的每一行，评估每一行的谓词，并输出主键与随机值匹配的所有行。这可能是零、一或多行，具体取决于`id`列值和中的值[`兰德（）`](mathematical-functions.html#function_rand)顺序。

    不确定性的影响不仅限于[`选择`](select.html)陈述。这[`更新`](update.html)语句使用非确定性函数来选择要修改的行：


```
UPDATE t SET col_a = some_expr WHERE id = FLOOR(1 + RAND() * 49);
```

据推测，目的是最多更新主键与表达式匹配的单行。但是，它可能会更新零、一或多行，具体取决于`id`列值和中的值[`兰德（）`](mathematical-functions.html#function_rand)顺序。

刚才描述的行为对性能和复制有影响：

-   因为非确定性函数不会产生常量值，所以优化器不能使用其他可能适用的策略，例如索引查找。结果可能是表扫描。

-   `InnoDB`可能会升级为范围键锁，而不是为匹配的行使用单行锁。

-   不确定性执行的更新对于复制来说是不安全的。

    困难源于以下事实[`兰德（）`](mathematical-functions.html#function_rand)函数对表的每一行计算一次。为避免多重功能评估，请使用以下技术之一：

-   将包含不确定函数的表达式移到单独的语句中，将值保存在变量中。在原始语句中，将表达式替换为对变量的引用，优化器可以将其视为常量值：

    ```
    SET @keyval = FLOOR(1 + RAND() * 49);
    UPDATE t SET col_a = some_expr WHERE id = @keyval;
    ```

-   将随机值分配给派生表中的变量。这种技术导致变量在用于比较之前被赋值一次。`在哪里`条款：

    ```
    UPDATE /*+ NO_MERGE(dt) */ t, (SELECT FLOOR(1 + RAND() * 49) AS r) AS dt
    SET col_a = some_expr WHERE id = dt.r;
    ```

    如前所述，在`在哪里`子句可能会阻止优化并导致表扫描。但是，可以部分优化`在哪里`子句如果其他表达式是确定性的。例如：


```
SELECT * FROM t WHERE partial_key=5 AND some_column=RAND();
```

如果优化器可以使用`部分密钥`减少选定的行集，[`兰德（）`](mathematical-functions.html#function_rand)执行的次数更少，从而减少了非确定性对优化的影响。
