MySQL 9.0 提供对服务器端预处理语句的支持。此支持利用了高效的客户端/服务器二进制协议。使用带有参数值占位符的预处理语句具有以下优点
以下部分概述了预处理语句的特征
应用程序中的预处理语句
您可以通过客户端编程接口使用服务器端预处理语句,包括用于 C 程序的 MySQL C API 客户端库、用于 Java 程序的 MySQL Connector/J 以及用于使用 .NET 技术的程序的 MySQL Connector/NET。例如,C API 提供了一组构成其预处理语句 API 的函数调用。请参见 C API 预处理语句接口。其他语言接口可以通过链接 C 客户端库来提供对使用二进制协议的预处理语句的支持,例如在 PHP 5.0 及更高版本中提供的 mysqli
扩展。
SQL 脚本中的预处理语句
提供了一种可选的 SQL 接口来使用预处理语句。此接口不像通过预处理语句 API 使用二进制协议那样高效,但无需编程,因为它直接在 SQL 级别可用。
当您无法使用编程接口时,可以使用它。
您可以从任何可以将 SQL 语句发送到服务器以执行的程序中使用它,例如 mysql 客户端程序。
即使客户端使用旧版本的客户端库,您也可以使用它。
预处理语句的 SQL 语法旨在用于以下情况
在编码之前测试预处理语句在您的应用程序中的工作方式。
当您没有访问支持预处理语句的编程 API 时,使用预处理语句。
交互式地排查与预处理语句相关的应用程序问题。
创建一个测试用例来重现与预处理语句相关的问题,以便您提交错误报告。
PREPARE、EXECUTE 和 DEALLOCATE PREPARE 语句
预处理语句的 SQL 语法基于三个 SQL 语句
PREPARE
准备一个语句以供执行(请参见 第 15.5.1 节,“PREPARE 语句”)。EXECUTE
执行一个预处理语句(请参见 第 15.5.2 节,“EXECUTE 语句”)。DEALLOCATE PREPARE
释放一个预处理语句(请参见 第 15.5.3 节,“DEALLOCATE PREPARE 语句”)。
以下示例显示了两种等效的方式来准备一个语句,该语句计算给定两边长度的三角形的斜边。
第一个示例展示了如何使用字符串字面量来提供语句文本创建预处理语句。
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
系统变量。要阻止使用预处理语句,请将值设置为 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
{CREATE | ALTER | DROP } EVENT
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 不支持以下作为预处理语句。
SHOW WARNINGS
、SHOW COUNT(*) WARNINGS
SHOW ERRORS
、SHOW COUNT(*) ERRORS
包含对
warning_count
或error_count
系统变量的任何引用的语句。
通常,SQL 预处理语句中不允许的语句在存储程序中也不允许。例外情况在 第 27.9 节,“存储程序的限制” 中说明。
对预处理语句引用的表或视图的元数据更改会被检测到,并会在下次执行语句时导致自动重新准备语句。有关更多信息,请参见 第 10.10.3 节,“预处理语句和存储程序的缓存”。
使用预处理语句时,可以使用占位符作为 LIMIT
子句的参数。请参见 第 15.2.13 节,“SELECT 语句”。
在包含事件 DDL 的预处理语句中不支持占位符。尝试在这样的语句中使用占位符会被 PREPARE
拒绝,并显示 ERROR 6413 (HY000): 动态参数只能用于 DML 语句。相反,您可以通过在存储过程主体中组装包含事件 SQL 的文本,并将 SQL 语句的任何可变部分作为 IN
参数传递给存储过程,以可重用的方式完成此操作;然后,您可以使用 PREPARE
语句准备组装好的文本(也位于存储过程主体中),然后使用所需的参数值调用该过程。请参见 第 15.1.13 节,“CREATE EVENT 语句”,了解一个示例。
在与 PREPARE
和 EXECUTE
一起使用的准备好的 CALL
语句中,从 MySQL 9.0 开始,支持 OUT
和 INOUT
参数的占位符。请参见 第 15.2.1 节,“CALL 语句”,了解早期版本的示例和解决方法。无论版本如何,都可以将占位符用于 IN
参数。
预处理语句的 SQL 语法不能以嵌套方式使用。也就是说,传递给 PREPARE
的语句本身不能是 PREPARE
、EXECUTE
或 DEALLOCATE PREPARE
语句。
预处理语句的 SQL 语法不同于使用预处理语句 API 调用。例如,您不能使用 mysql_stmt_prepare()
C API 函数准备 PREPARE
、EXECUTE
或 DEALLOCATE PREPARE
语句。
预处理语句的 SQL 语法可以在存储过程中使用,但不能在存储函数或触发器中使用。但是,游标不能用于使用 PREPARE
和 EXECUTE
准备和执行的动态语句。游标的语句在游标创建时进行检查,因此语句不能是动态的。
预处理语句的 SQL 语法不支持多语句(即,用 ;
字符分隔的单个字符串中的多个语句)。
要编写使用 CALL
SQL 语句执行包含预处理语句的存储过程的 C 程序,必须启用 CLIENT_MULTI_RESULTS
标志。这是因为每个 CALL
都返回一个结果来指示调用状态,以及过程内执行的语句可能返回的任何结果集。
CLIENT_MULTI_RESULTS
可以在您调用 mysql_real_connect()
时启用,可以通过显式传递 CLIENT_MULTI_RESULTS
标志本身,或隐式传递 CLIENT_MULTI_STATEMENTS
(这也启用 CLIENT_MULTI_RESULTS
)来实现。有关更多信息,请参见 第 15.2.1 节,“CALL 语句”。