MySQL 准备语句
在 MySQL版本 4.1之前,每个查询以文本格式发送到MySQL服务器,并使用文本协议将数据返回给客户端。在将结果返回给客户端之前,MySQL已完全解析了查询并将结果集转换为
string 。在这里,解析意味着对提交的查询进行了语法和语义上的验证,并且特权也得到了验证。
将数据返回给客户端的文本协议存在严重的性能问题。为了解决这个问题,MySQL从4.1版本开始提供了一种称为"预处理语句"的新功能。
预处理语句或参数化语句用于高效重复执行相同的语句。它利用了客户端/服务器二进制协议。 Prepared语句将包含
占位符(?)的查询传递给MySQL Server。请参见以下示例:
mysql> SELECT * FROM student WHERE studentId = ?;
当MySQL使用不同的
studentId 值执行上述语句时,它无法完全分析该语句。结果, MySQL 将更快地执行该语句,尤其是当它多次执行相同的查询时。
准备好的语句包含占位符(?),这有助于避免许多 SQL 注入变体,并使我们的应用程序更安全。
预处理语句的优点
以下是MySQL中预处理语句的优点:
我们可以重复执行多次准备好的语句。
每次执行时,都会评估绑定变量的当前值并将其发送到服务器。该语句不会再次解析。语句模板不会再次传输到服务器。
准备好的语句的基本工作流程
准备好的语句的基本工作流程主要包括两个阶段。但是,它具有一个可选阶段,总结如下:
PREPARE
执行
取消分配(可选)
PREPARE阶段
在准备阶段,将
语句模板发送到数据库服务器。服务器执行语法检查并初始化内部服务器资源以供以后使用。简而言之,它准备执行语句。
语法
以下是准备阶段的语法:
PREPARE stmt_name FROM preparable_stmt;
执行阶段
在执行阶段,
客户端绑定参数值并将其发送到服务器。服务器根据语句模板和绑定值创建一条语句,以使用先前创建的内部资源执行该语句。简而言之,一旦prepared语句准备好查询,我们就可以执行该查询。
语法
以下是执行prepared语句的语法声明:
EXECUTE stmt_name [USING @var_name [, @var_name]....]
DEALLOCATE/DROP阶段
这是最后一个可选阶段,用于
释放准备好的语句。
语法
以下是取消分配准备好的语句的语法:
{DEALLOCATE | DROP} PREPARE stmt_name;
与准备好的语句有关的关键点
在一个会话中创建的准备好的语句不适用于其他会话。这意味着准备好的语句是特定于会话的。
会话结束时,无论是正常会话还是异常会话,其准备好的语句在内存中都不存在。
在存储程序中创建的准备好的语句在程序完成执行后仍然存在,以后可以在程序外执行。
MySQL准备语句示例
让我们通过一些示例来了解如何使用MySQL准备语句。
在这里,我们要直接在PREPARE语句的帮助下创建一条语句,如下所示:
mysql> PREPARE stmt1 FROM 'SELECT ?+? AS SUM';
接下来,我们将两个值分配给两个可用于占位符(?)的变量:
mysql> SET @a = 20;
mysql> SET @b = 30;
现在,我们可以借助EXECUTE语句来执行查询:
mysql> EXECUTE stmt1 USING @a, @b;
执行后,我们将获得结果
总和。参见下图以了解结果:
以下示例将使用示例数据库中的
employee 表,其中包含以下数据。
首先,我们将准备一个声明,该声明返回由员工
id 指定的员工
名称和
名称:
mysql> PREPARE stmt1 FROM
'SELECT Name, Designation FROM employee
WHERE Emp_id = ?';
接下来,我们需要声明一个名为id的变量并将其值设置为'1':
现在,我们可以在EXECUTE语句的帮助下执行准备好的语句:
mysql> EXECUTE stmt1 USING @id;
执行后,我们将获得包含员工姓名和职务的结果。参见下图以了解查询的执行:
同样,我们将分配变量ID的另一个值:
现在,使用新的员工ID执行准备好的语句。我们将看到如下输出:
最后,我们可以释放已准备好的手动声明。但是,它们将在会话关闭时自动删除。
mysql> DEALLOCATE PREPARE stmt1;
如果在执行上述查询后尝试执行准备好的语句,则会收到如下错误:
如何在存储过程中使用预处理语句?
我们可以通过在
BEGIN 和
END 块。我们可以通过创建一个通过将表名作为存储过程的参数传递来返回表中所有记录的示例来理解它。
按如下所示创建存储过程:
DELIMITER $
CREATE PROCEDURE tbl_detail(tab_name Varchar(40))
BEGIN
SET @A:= CONCAT('Select * from',' ',tab_name);
Prepare stmt FROM @A;
EXECUTE stmt;
END$
DELIMITER ;
请参见下图执行存储过程:
之后成功创建后,我们可以通过将表名称指定为其参数来
调用该过程。
mysql> CALL tbl_detail('employee');
它将显示该表的所有记录。参见下图:
语句与准备语句
以下是MySQL中的语句和预准备语句之间的主要区别:
声明 |
准备好的声明 |
当我们只想执行一次SQL查询时使用。 |
在我们要多次执行SQL查询时使用。 |
它用于DDL语句。 |
它可用于任何SQL查询。 |
它不能用于读写二进制数据。 |
它可以用来读写二进制数据。 |
这是静态的,这意味着我们无法在运行时传递参数。 |
它是动态的,这意味着我们可以在运行时传递参数。 |
执行性能很慢。 |
执行性能很快。 |
它不会阻止SQL注入。 |
它有助于防止SQL注入攻击。 |
它使用文本协议进行通讯。 |
它使用二进制协议进行通信。 |
存储过程与预准备语句
以下是存储过程与MySQL中预准备语句之间的主要区别:
存储过程 |
准备好的声明 |
存储过程是访问关系数据库管理系统的一系列SQL语句。 |
准备的语句是包含占位符而不是实际值的查询。 |
它可以存储在数据库服务器中。 |
它不能存储在数据库中。 |