预编译语句
预编译语句(Prepared Statement)把 SQL 的解析和编译提前完成,之后再用不同参数重复执行——省时又安全。
一次 SQL 执行的完整流程:解析、编译优化、执行、返回结果。普通 SQL 每次都要走全流程。预编译语句把前两步缓存起来,之后的执行直接从第三步开始。
预编译语句的四大优势
- 减少解析时间:查询模板只解析编译一次,后续执行直接复用
- 最小化带宽:只需发送参数值,无需重复发送完整 SQL 语句
- 防止 SQL 注入:参数值通过不同协议传输,不会被当作 SQL 代码执行
- 代码更清晰:SQL 命令与数据完全分离,逻辑更清晰
MySQL 中预编译语句的生命周期:PREPARE 定义模板 → EXECUTE 执行(可多次)→ DEALLOCATE PREPARE 释放。预编译语句在会话内有效,断开连接后自动释放。
参数类型绑定
在应用层使用预编译语句时,需要指定每个参数的数据类型:
| 类型标记 | 含义 | 示例 |
|---|---|---|
i | 整数(integer) | bind_param("i", $age) |
d | 双精度浮点数(double) | bind_param("d", $price) |
s | 字符串(string) | bind_param("s", $name) |
b | 二进制数据(blob) | bind_param("b", $image) |
明确指定参数类型可以进一步降低 SQL 注入风险,因为数据库知道每个参数应该是什么类型。
以飞翔科技为例。批量插入员工数据,用预编译语句减少编译开销:
-- 创建预编译语句
PREPARE insert_emp FROM
'INSERT INTO employees (emp_name, dept_code, basic_salary, join_year)
VALUES (?, ?, ?, ?)';
-- 反复执行,每次换参数
SET @name = '翱翔', @dept = 2, @salary = 8888.88, @year = 2018;
EXECUTE insert_emp USING @name, @dept, @salary, @year;
SET @name = '飞翔', @dept = 1, @salary = 20000.00, @year = 2018;
EXECUTE insert_emp USING @name, @dept, @salary, @year;
SET @name = '飞飞', @dept = 3, @salary = 6000.00, @year = 2020;
EXECUTE insert_emp USING @name, @dept, @salary, @year;
-- 释放预编译语句
DEALLOCATE PREPARE insert_emp;
在 Python 中(更贴近实际开发):
cursor.execute("""PREPARE insert_emp FROM 'INSERT INTO employees VALUES (?, ?, ?, ?)'""")
cursor.executemany("EXECUTE insert_emp USING (%s, %s, %s, %s)", [
('翱翔', 2, 8888.88, 2018),
('飞翔', 1, 20000.00, 2018),
('飞飞', 3, 6000.00, 2020),
])
预编译语句的两个核心价值:一是防 SQL 注入(参数永远不会被当作代码执行),二是高并发下性能优势明显。飞翔科技的批量数据处理(如每日工资结算、月度报表生成)都会用到它。