15.5 预处理语句
MySQL 8.4 支持服务器端预处理语句。这项支持利用了高效的客户端/服务器二进制协议。使用预处理语句,带有占位符的参数值具有以下优点:
以下部分提供了预处理语句的特征概述:
预处理语句 in Application Programs
您可以通过客户端编程接口使用服务器端预处理语句,例如C API客户端库、Java程序中的MySQL Connector/J、.NET技术中的MySQL Connector/NET。例如,C API提供了一组函数调用,组成了其预处理语句API。见C API Prepared Statement Interface。其他语言接口可以通过链接C客户端库来提供支持预处理语句的功能,例如PHP中的mysqli扩展。
预处理语句 in SQL Scripts
另一种SQL接口可用于预处理语句。这项接口不如使用二进制协议的预处理语句API高效,但不需要编程,因为它可以在SQL级别直接访问:
-
您可以在没有编程接口的情况下使用它。
-
您可以从任何可以将 SQL 语句发送到服务器以执行的程序中使用它,例如mysql 客户端程序。
-
您可以使用它,即使客户端使用的是老版本的客户端库。
预先编译语句的 SQL 语法旨在用于以下情况:
-
测试在您的应用程序中使用预先编译语句之前编写代码。
-
在您没有访问支持预先编译语句的编程 API 的情况下使用预先编译语句。
-
交互式地 troubleshoot 应用程序问题,以使用预先编译语句。
-
创建一个重现预先编译语句问题的测试用例,以便提交错误报告。
PREPARE, EXECUTE, and 释放预处理语句s
预先编译语句的 SQL 语法基于三个 SQL 语句:
-
PREPARE
准备语句以执行(见第15.5.1节,“PREPARE 语句”)。 -
EXECUTE
执行预先编译语句(见第15.5.2节,“EXECUTE 语句”)。 -
DEALLOCATE PREPARE
释放预先编译语句(见第15.5.3节,“DEALLOCATE PREPARE 语句”)。
以下示例展示了两个等效的方式来准备一个计算三角形的 hypotenuse 的语句,该语句使用两个边的长度。
第一个示例显示了如何使用字符串字面量来提供语句的文本:
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 Syntax Permitted in 预处理语句
以下 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 不支持以下语句作为预先编译语句:
-
SHOW WARNINGS
,SHOW COUNT(*) WARNINGS
-
SHOW ERRORS
,SHOW COUNT(*) ERRORS
-
包含任何对
warning_count
或error_count
系统变量的语句。
通常,不能在SQL预处理语句中使用的语句也不能在存储程序中使用。除非在第27.8节,“存储程序限制”中所述的例外情况。
当预处理语句中涉及到表或视图的元数据变化时,MySQL会检测到这种变化,并自动重新准备语句,以便在下一次执行时使用。更多信息,请见第10.10.3节,“预处理语句和存储程序的缓存”。
在使用预处理语句时,可以使用占位符来代替LIMIT子句的参数。请见第15.2.13节,“SELECT语句”。
在使用CALL
语句时,可以使用占位符来代替OUT和INOUT参数。从MySQL 8.4开始可用。请见第15.2.1节,“CALL语句”,了解详细信息和早期版本的解决方法。无论版本,可以使用占位符来代替IN参数。
预处理语句的SQL语法不能在嵌套使用。也就是说,不能将语句传递给PREPARE
,EXECUTE
或DEALLOCATE PREPARE
语句。
预处理语句的SQL语法与使用预处理语句API调用不同。例如,不能使用mysql_stmt_prepare()
C API函数来准备PREPARE
,EXECUTE
或DEALLOCATE PREPARE
语句。
预处理语句可以在存储程序中使用,但不能在存储函数或触发器中使用。然而,不能使用游标来执行动态语句,该语句使用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 Statement”。