首页/数据/数据库速查/预处理语句 PREPARE / EXECUTE / DEALLOCATE
SQL 语法52 条

预处理语句 PREPARE / EXECUTE / DEALLOCATE

服务端预处理可复用于高频 SQL;应用层参数化查询(占位符)是防注入的根本手段,比手工拼接字符串安全得多。

数据库速查数据更新于 2026-10-13

预处理语句是什么

预处理把 SQL 的「结构」与「数据」分开:先 PREPARE 一条带占位符的语句让服务端解析成执行计划,再用 EXECUTE 反复传入参数执行。对应用来说,真正重要的是第二条:用占位符传参是防 SQL 注入的根本手段,因为参数永远被当成数据解析,不会被拼进语法结构里。服务端 PREPARE 是 SQL 层的工作方式,应用层的参数化查询由驱动实现,二者不要混为一谈。

基本信息

SQL 层语法PREPARE 名 FROM '语句';EXECUTE 名 USING 变量;DEALLOCATE PREPARE 名
变量形式EXECUTE USING 只接受用户变量(@var)或字面量,不接受表达式
作用域预处理语句是会话级的,断开连接即失效
数量上限max_prepared_stmt_count(默认 16382),泄漏句柄会打满
语句限制只有文档列出的语句类型可预处理,动态表名与列名不能参数化

典型使用场景

  • 防注入:应用所有带用户输入的查询统一走占位符,杜绝字符串拼接
  • 一次解析多次执行:批量写入或循环查询时复用同一条语句,省去重复解析
  • SQL 层临时脚本:在 mysql 客户端里用 PREPARE 拼装需要动态条件的语句做临时核对
  • 安全审计:发现代码里仍存在拼接 SQL 时,改用参数化并配合代码扫描固化规则
  • 连接池行为验证:确认驱动是否真的使用了服务端预处理,还是仅在客户端做转义

SQL 层与错误用法对照

SQL 层与错误用法对照
-- 正确:占位符只用于「值」
PREPARE stmt FROM 'SELECT id, sku FROM orders WHERE user_id = ? LIMIT ?';
SET @uid = 42, @n = 10;
EXECUTE stmt USING @uid, @n;
DEALLOCATE PREPARE stmt;   -- 用完立刻释放,避免句柄泄漏
-- 错误:想让占位符充当表名,服务端不接受这种参数
-- PREPARE bad FROM 'SELECT * FROM ?';
-- 动态表名只能在白名单校验后拼接
SET @tbl = 'orders_2026';

应用层参数化

应用层参数化
# 参数与语句分开传,驱动负责转义与协议编码
cur.execute('SELECT id, sku FROM orders WHERE user_id = %s AND status = %s', (uid, status))
# 反例:f-string 拼接等于把注入入口留在代码里
cur.execute(f"SELECT * FROM orders WHERE sku = '{sku}'")

拼接与参数化对照

维度手工拼接字符串占位符参数化
注入风险随输入内容变化,始终存在参数只作数据解析,注入不可达
计划复用每个不同值都是新语句同一语句可复用计划,高频查询更省
可读性引号与转义层层叠加语句与参数分离,容易审阅
适用边界仅动态标识符场景(需白名单)所有可传值的位置
服务端预处理在连接内有效。使用连接池时,连接可能被复用给别的请求,长事务与异常路径都要确保语句被 DEALLOCATE,否则句柄会累积到 max_prepared_stmt_count 上限报错。
参数化不覆盖标识符:表名、列名、ORDER BY 字段这类位置无法用占位符。需要动态拼接时必须做白名单校验(只允许预定义集合里的名字),不要仅依赖转义函数。

易错点

  • 试图用占位符传表名或列名:服务端会直接报语法错误,最后往往退化成字符串拼接,正确做法是白名单校验
  • 预处理语句用完不释放:句柄累积打满 max_prepared_stmt_count,表现为大量查询突然报「Can't create more than max_prepared_stmt_count statements」
  • 以为用了 ORM 就自动安全:拼接原生 SQL 片段(如 raw 查询、order by 字符串)仍会引入注入点,需要单独审查
  • 把预处理当成性能银弹:单次执行的查询用预处理收益有限,收益来自同语句的高频重复执行
  • 在只读连接上执行 PREPARE 相关语句却忽略权限差异:不同账号对可预处理语句集合的支持一致,但权限校验仍按实际执行者进行,越权查询会被拒绝

数据来源与参考

  • MySQL 8.0 Reference Manual: PREPARE Statement

相关工具

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌