## 13.5 准备好的报表

[13.5.1 PREPARE 语句](prepare.html)
[13.5.2 执行语句](execute.html)
[13.5.3 DEALLOCATE PREPARE 语句](deallocate-prepare.html)

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

MySQL 8.0 提供对服务器端预处理语句的支持。这种支持利用了高效的客户端/服务器二进制协议。对参数值使用带有占位符的预处理语句具有以下好处：

-   每次执行语句时解析语句的开销更少。通常，数据库应用程序处理大量几乎相同的语句，仅更改子句中的文字或变量值，例如`在哪里`对于查询和删除，`放`用于更新，以及`价值观`用于插入。

-   防止 SQL 注入攻击。参数值可以包含未转义的 SQL 引号和分隔符。

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

    以下部分概述了准备好的语句的特征：

-   [应用程序中的准备语句](sql-prepared-statements.html#prepared-statements-in-applications)

-   [SQL 脚本中的预处理语句](sql-prepared-statements.html#prepared-statements-in-scripts)

-   [PREPARE、EXECUTE 和 DEALLOCATE PREPARE 语句](sql-prepared-statements.html#prepared-statement-types)

-   [准备好的语句中允许的 SQL 语法](sql-prepared-statements.html#prepared-statements-permitted)

### 应用程序中的准备语句

您可以通过客户端编程接口使用服务器端准备好的语句，包括[MySQL C API 客户端库](https://dev.mysql.com/doc/c-api/8.0/en/)对于 C 程序，[MySQL 连接器/J](https://dev.mysql.com/doc/connector-j/8.0/en/)对于 Java 程序，以及[MySQL 连接器/网络](https://dev.mysql.com/doc/connector-net/en/)对于使用 .NET 技术的程序。例如，C API 提供了一组函数调用，这些函数调用构成了它的预处理语句 API。看[C API 准备语句接口](https://dev.mysql.com/doc/c-api/8.0/en/c-api-prepared-statement-interface.html).其他语言接口可以通过在 C 客户端库中的链接为使用二进制协议的预处理语句提供支持，例如[`mysqli`延期](http://php.net/mysqli), 在 PHP 5.0 及更高版本中可用。

### SQL 脚本中的预处理语句

可以使用预准备语句的替代 SQL 接口。此接口不如通过准备好的语句 API 使用二进制协议高效，但不需要编程，因为它可以直接在 SQL 级别使用：

-   当您没有可用的编程接口时，您可以使用它。

-   您可以从任何可以将 SQL 语句发送到服务器以执行的程序中使用它，例如[**mysql**](mysql.html)客户端程序。

-   即使客户端使用的是旧版本的客户端库，您也可以使用它。

    准备好的语句的 SQL 语法旨在用于以下情况：

-   在编码之前测试准备好的语句在应用程序中的工作方式。

-   在您无法访问支持它们的编程 API 时使用准备好的语句。

-   使用准备好的语句交互式地解决应用程序问题。

-   创建一个用准备好的语句重现问题的测试用例，以便您可以提交错误报告。

### PREPARE、EXECUTE 和 DEALLOCATE PREPARE 语句

准备好的语句的 SQL 语法基于三个 SQL 语句：

-   [`准备`](prepare.html)准备执行语句（参见[第 13.5.1 节，“准备声明”](prepare.html)）。

-   [`执行`](execute.html)执行一个准备好的语句（见[第 13.5.2 节，“执行语句”](execute.html)）。

-   [`解除分配准备`](deallocate-prepare.html)发布一个准备好的语句（见[第 13.5.3 节，“DEALLOCATE PREPARE 语句”](deallocate-prepare.html)）。

    下面的例子展示了两种等效的方法来准备一个计算三角形斜边给定两条边的长度的语句。

    第一个示例展示了如何通过使用字符串文字来提供语句的文本来创建预准备语句：


```
mysql> PREPARE stmt1 FROM 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse';
mysql> SET @a = 3;
mysql> SET @b = 4;
mysql> EXECUTE stmt1 USING @a, @b;
+------------+
| hypotenuse |
+------------+
|          5 |
+------------+
mysql> DEALLOCATE PREPARE stmt1;
```

第二个示例类似，但将语句文本作为用户变量提供：

```
mysql> SET @s = 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse';
mysql> PREPARE stmt2 FROM @s;
mysql> SET @a = 6;
mysql> SET @b = 8;
mysql> EXECUTE stmt2 USING @a, @b;
+------------+
| hypotenuse |
+------------+
|         10 |
+------------+
mysql> DEALLOCATE PREPARE stmt2;
```

这是一个附加示例，演示如何通过将表名称存储为用户变量来选择在运行时执行查询的表：

```
mysql> USE test;
mysql> CREATE TABLE t1 (a INT NOT NULL);
mysql> INSERT INTO t1 VALUES (4), (8), (11), (32), (80);

mysql> SET @table = 't1';
mysql> SET @s = CONCAT('SELECT * FROM ', @table);

mysql> PREPARE stmt3 FROM @s;
mysql> EXECUTE stmt3;
+----+
| a  |
+----+
|  4 |
|  8 |
| 11 |
| 32 |
| 80 |
+----+

mysql> DEALLOCATE PREPARE stmt3;
```

准备好的语句特定于创建它的会话。如果您终止会话而不释放先前准备的语句，则服务器会自动释放它。

准备好的语句对于会话也是全局的。如果在存储例程中创建准备好的语句，则在存储例程结束时不会释放它。

为了防止同时创建太多准备好的语句，设置[`max_prepared_stmt_count`](server-system-variables.html#sysvar_max_prepared_stmt_count)系统变量。要防止使用准备好的语句，请将值设置为 0。

### 准备好的语句中允许的 SQL 语法

以下 SQL 语句可用作预处理语句：

```
ALTER TABLE
ALTER USER
ANALYZE TABLE
CACHE INDEX
CALL
CHANGE MASTER
CHECKSUM {TABLE | TABLES}
COMMIT
{CREATE | DROP} INDEX
{CREATE | RENAME | DROP} DATABASE
{CREATE | DROP} TABLE
{CREATE | RENAME | DROP} USER
{CREATE | DROP} VIEW
DELETE
DO
FLUSH {TABLE | TABLES | TABLES WITH READ LOCK | HOSTS | PRIVILEGES
  | LOGS | STATUS | MASTER | SLAVE | USER_RESOURCES}
GRANT
INSERT
INSTALL PLUGIN
KILL
LOAD INDEX INTO CACHE
OPTIMIZE TABLE
RENAME TABLE
REPAIR TABLE
REPLACE
RESET {MASTER | SLAVE}
REVOKE
SELECT
SET
SHOW BINLOG EVENTS
SHOW CREATE {PROCEDURE | FUNCTION | EVENT | TABLE | VIEW}
SHOW {MASTER | BINARY} LOGS
SHOW {MASTER | SLAVE} STATUS
SLAVE {START | STOP}
TRUNCATE TABLE
UNINSTALL PLUGIN
UPDATE
```

不支持其他语句。

为了符合 SQL 标准，该标准指出诊断语句不可准备，MySQL 不支持以下作为准备好的语句：

-   `显示警告`,`显示计数（*）警告`

-   `显示错误`,`显示计数 (*) 错误`

-   包含任何引用的声明[`警告计数`](server-system-variables.html#sysvar_warning_count)要么[`错误计数`](server-system-variables.html#sysvar_error_count)系统变量。

    通常，SQL 准备语句中不允许的语句在存储程序中也不允许。例外情况在[第 25.8 节，“对存储程序的限制”](stored-program-restrictions.html).

    检测到准备好的语句引用的表或视图的元数据更改，并在下次执行时自动重新准备该语句。有关详细信息，请参阅[第 8.10.3 节，“准备语句和存储程序的缓存”](statement-caching.html).

    占位符可用于`限制`使用准备好的语句时的子句。看[第 13.2.10 节，“SELECT 语句”](select.html).

    在准备中[`称呼`](call.html)使用的语句[`准备`](prepare.html)和[`执行`](execute.html), 占位符支持`出去`和`进出`参数从 MySQL 8.0 开始可用。看[第 13.2.1 节，“CALL 语句”](call.html)，例如和早期版本的解决方法。占位符可用于`在`参数与版本无关。

    准备好的语句的 SQL 语法不能以嵌套方式使用。也就是说，传递给[`准备`](prepare.html)本身不能是[`准备`](prepare.html),[`执行`](execute.html)， 要么[`解除分配准备`](deallocate-prepare.html)陈述。

    准备好的语句的 SQL 语法不同于使用准备好的语句 API 调用。例如，您不能使用[`mysql_stmt_prepare()`](https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-prepare.html)C API函数编写一个[`准备`](prepare.html),[`执行`](execute.html)， 要么[`解除分配准备`](deallocate-prepare.html)陈述。

    预准备语句的 SQL 语法可以在存储过程中使用，但不能在存储函数或触发器中使用。但是，游标不能用于准备和执行的动态语句[`准备`](prepare.html)和[`执行`](execute.html).游标的语句是在创建游标时检查的，因此该语句不能是动态的。

    准备好的语句的 SQL 语法不支持多语句（即，单个字符串中的多个语句由`;`人物）。

    编写使用[`称呼`](call.html)SQL 语句来执行包含准备好的语句的存储过程，`CLIENT_MULTI_RESULTS`标志必须启用。这是因为每个[`称呼`](call.html)除了过程中执行的语句可能返回的任何结果集之外，还返回一个结果以指示调用状态。

`CLIENT_MULTI_RESULTS`打电话时可以启用[`mysql_real_connect()`](https://dev.mysql.com/doc/c-api/8.0/en/mysql-real-connect.html)，或者通过明确地传递`CLIENT_MULTI_RESULTS`标记自身，或通过传递`CLIENT_MULTI_STATEMENTS`（这也使`CLIENT_MULTI_RESULTS`）。有关其他信息，请参阅[第 13.2.1 节，“CALL 语句”](call.html).
