Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 Reference Manual  /  SQL Statements  /  Prepared Statements

15.5 预处理语句

MySQL 8.4 支持服务器端预处理语句。这项支持利用了高效的客户端/服务器二进制协议。使用预处理语句,带有占位符的参数值具有以下优点:

  • 减少每次执行语句时的解析开销。通常,数据库应用程序处理大量几乎相同的语句,只是将字面值或变量值在子句中进行更改,如WHERE子句、DELETE子句、SET子句和VALUES子句。

  • 保护SQL注入攻击。参数值可以包含未转义的SQL引号和分隔符字符。

以下部分提供了预处理语句的特征概述:

预处理语句 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 语句:

以下示例展示了两个等效的方式来准备一个计算三角形的 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_counterror_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语法不能在嵌套使用。也就是说,不能将语句传递给PREPAREEXECUTEDEALLOCATE PREPARE语句。

预处理语句的SQL语法与使用预处理语句API调用不同。例如,不能使用mysql_stmt_prepare()C API函数来准备PREPAREEXECUTEDEALLOCATE PREPARE语句。

预处理语句可以在存储程序中使用,但不能在存储函数或触发器中使用。然而,不能使用游标来执行动态语句,该语句使用PREPAREEXECUTE。游标语句在游标创建时被检查,因此不能是动态语句。

预备语句的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”