MySQL 8.0 支持服务器端预编译语句。这种支持利用了高效的客户端/服务器二进制协议。对参数值使用占位符的预编译语句有以下好处: ● 每次执行语句时解析语句的开销更小。通常,数据库应用程序处理大量几乎相同的语句,只对子句中的字面量或变量值进行更改,例如用于查询和删除的 WHERE 子句、用于更新 SET 子句和用于插入的 VALUES 子句。 ● 防止 SQL 注入攻击。参数值可以包含未转义的 SQL 引号和分隔符字符。 以下各部分概述了预编译语句的特点: ● 应用程序中的预编译语句 ● SQL脚本中的预编译语句 ● PREPARE、EXECUTE 和 DEALLOCATE PREPARE 语句 ● 预编译语句中允许的SQL语法 应用程序中的预编译语句 可以通过客户端编程接口使用服务器端预编译语句,这些接口包括用于C程序的 MySQL C API 客户端库、用于 Java 程序的 MySQL Connector/J 以及用于使用 .NET 技术的程序的 MySQL Connector/NET。例如,C API 提供了一组函数调用,这些函数调用构成了其预编译语句 API。其他语言接口可以通过链接C客户端库来为使用二进制协议的预编译语句提供支持,例如在PHP5.0和更高版本中提供的 mysqli 扩展。 SQL 脚本中的预编译语句 预编译语句的另一个SQL接口。此接口的效率不如通过预编译语句 API 使用二进制协议,但不需要编程,因为它直接在SQL级别可用: ● 可以在没有编程接口时使用它。 ● 可以从任何可以将SQL语句发送到要执行的服务器的程序中使用它,例如 mysql 客户端程序。 ● 即使客户端使用的是旧版本的客户端库,也可以使用它。 预编译语句的SQL语法用于以下情况: ● 在编写应用程序之前测试预编译语句。 ● 当无法访问支持这些语句的编程API时使用这些语句。 ● 使用预编译语句以交互方式解决应用程序问题。 ● 创建一个测试用例,用预编译语句重现问题,以便提交错误报告。 PREPARE、EXECUTE 和 DEALLOCATE PREPARE 语句 预编译语句的SQL语法基于三个SQL语句: ● PREPARE 预编译执行语句 ● EXECUTE 执行预编译语句 ● 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 TABLEALTER USERANALYZE TABLECACHE INDEXCALLCHANGE MASTERCHECKSUM {TABLE | TABLES}COMMIT{CREATE | DROP} INDEX{CREATE | RENAME | DROP} DATABASE{CREATE | DROP} TABLE{CREATE | RENAME | DROP} USER{CREATE | DROP} VIEWDELETEDOFLUSH {TABLE | TABLES | TABLES WITH READ LOCK | HOSTS | PRIVILEGES | LOGS | STATUS | MASTER | SLAVE | USER_RESOURCES}GRANTINSERTINSTALL PLUGINKILLLOAD INDEX INTO CACHEOPTIMIZE TABLERENAME TABLEREPAIR TABLEREPLACERESET {MASTER | SLAVE}REVOKESELECTSETSHOW {WARNINGS | ERRORS}SHOW BINLOG EVENTSSHOW CREATE {PROCEDURE | FUNCTION | EVENT | TABLE | VIEW}SHOW {MASTER | BINARY} LOGSSHOW {MASTER | SLAVE} STATUSSLAVE {START | STOP}TRUNCATE TABLEUNINSTALL PLUGINUPDATE
不支持其他语句。 兼容 SQL 标准,诊断语句不可预编译,MySQL 不支持以下语句用作预编译语句: ● SHOW WARNINGS, SHOW COUNT(*) WARNINGS ● SHOW ERRORS, SHOW COUNT(*) ERRORS ● 包含对 warning_count 或 error_count 系统变量的任何引用的语句。 通常,SQL预编译语句中不允许的语句在存储程序中也不允许。 检测到对预编译语句引用的表或视图的元数据更改,并在下一次执行语句时自动重新编译语句。 使用预编译语句时,占位符可用于 LIMIT 子句的参数。 在与 PREPARE 和 EXECUTE 一起使用的预编译 CALL 语句中,从 MySQL 8.0 开始支持对 OUT 和 INOUT 参数使用占位符。在任何版本中,占位符都可以用于 IN 参数。 预编译语句的 SQL 语法不能以嵌套方式使用。也就是说,传递给 PREPARE的语句本身不能是 PREPARE、EXECUTE 或 DEALLOCATE PREPARE 语句。 预编译语句的 SQL 语法不同于使用预编译语句 API 调用。例如,不能使用mysql_stmt_prepare() C API 函数来预编译 PREPARE、EXECUTE 或DEALLOCATE PREPARE 语句。 预编译语句的 SQL 语法可以在存储过程中使用,但不能在存储函数或触发器中使用。但是,游标不能用于使用 PREPARE 和 EXECUTE 预编译和执行的动态语句。游标的语句在游标创建时被检查,因此该语句不能是动态的。 预编译语句的SQL语法不支持多语句(即在一个由;字符分隔的字符串中有多个语句)。 要编写C程序,在其中使用 CALL SQL 语句执行包含预编译语句的存储过程,必须启用 CLIENT_MULTI_RESULTS 标志。这是因为除了过程中执行的语句可能返回的任何结果集外,每个调用都返回一个结果来指示调用状态。 当调用 mysql_real_connect() 时,可以通过显式传递CLIENT_MULTI_RESULTS 标志本身来启用 CLIENT_MULTI_RESULTS,也可以通过传递 CLIENT_MULTI_STATEMENTS 隐式传递CLIENT_MULTI_RESULTS(这也可以启用 CLIENT_MULTI_RESULTS)。