MySQL 8.0支持服务器端预处理语句。这种支持利用了有效的客户端/服务器二进制协议。对参数值使用带占位符的预处理语句具有以下好处:
- 每次执行语句时解析语句的开销都较小。通常,数据库应用程序处理大量几乎相同的语句,仅对子句中的文字或变量值进行更改,例如
WHERE
查询和删除,SET
更新和VALUES
插入。 - 防止SQL注入攻击。参数值可以包含未转义的SQL引号和定界符。
应用程序中的预处理的语句
您可以通过客户端编程接口使用服务器端预处理的语句,包括用于C程序的MySQL C API客户端库,用于Java程序的MySQL Connector / J和用于使用.NET技术的程序的MySQL Connector / NET。例如,C API提供了一组函数调用,这些函数构成了其预处理的语句API。请参见“ C API预处理语句”。其他语言接口可以通过在C客户端库中进行链接来为使用二进制协议的预处理的语句提供支持,其中一个示例就是mysqli
extension,在PHP 5.0及更高版本中可用。
SQL脚本中的预备语句
提供了预处理的语句的替代SQL接口。此接口的效率不如通过预预处理的语句API使用二进制协议有效,但不需要编程,因为它可以直接在SQL级别使用:
- 如果没有可用的编程接口,则可以使用它。
- 您可以从任何可以将SQL语句发送到要执行的服务器的程序(例如mysql客户端程序)中使用它。
- 即使客户端使用旧版本的客户端库,也可以使用它,只要您连接到运行MySQL 4.1或更高版本的服务器即可。
预处理语句的SQL语法旨在用于以下情况:
- 在对预备语句进行编码之前,先对其进行测试。
- 在无权访问支持它们的编程API时使用预处理的语句。
- 使用预处理的语句以交互方式解决应用程序问题。
- 创建一个测试案例,该测试案例用预处理的语句重现问题,以便您可以提交错误报告。
PREPARE,EXECUTE和DEALLOCATE PREPARE语句
预预处理语句的SQL语法基于以下三个SQL语句:
PREPARE
预处理要执行的语句(请参见“ PREPARE语句”)。EXECUTE
执行一个预处理的语句(请参见“ EXECUTE语句”)。DEALLOCATE PREPARE
释放预处理的语句(请参见“ 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 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 {WARNINGS | ERRORS} 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
系统变量的任何引用的语句。
MySQL 8.0不支持其他语句。
通常,在存储程序中也不允许使用SQL预预处理语句中不允许的语句。“对存储程序的限制”中指出了异常。
检测到由预处理的语句引用的表或视图的元数据更改,并在下一次执行该语句时导致该语句的自动重新预处理。有关更多信息,请参见“缓存预处理的语句和存储的程序”。
LIMIT
使用预处理的语句时,可以将占位符用作子句的参数。请参见“ SELECT语句”。
在CALL
与PREPARE
和一起使用的预处理的语句中EXECUTE
,从MySQL 8.0开始提供对OUT
和INOUT
参数的占位符支持。有关早期版本的示例和解决方法,请参见“ 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
)。有关更多信息,请参见“ CALL语句”。