SQL 参数传递机制详解

2次阅读

mysql原生只支持?位置参数,:name等命名参数需驱动层转换;混用会报错;存储过程in/out/inout决定参数流向;sp_executesql比exec更安全因强制分离sql与参数;mybatis推荐@param显式命名。

SQL 参数传递机制详解

MySQL 里用 ?:name 传参,到底该选哪个?

MySQL 原生不支持命名参数(如 :age),只认位置占位符 ?;但很多客户端(如 pdo、MyBatis、JDBC)在驱动层做了封装,让你能写 :name@name,最终还是被转成 ? + 顺序绑定。所以「语法看着像命名,底层仍是位置」是常态。

  • ? 最稳妥:所有 MySQL 客户端都原生支持,比如 PREPARE stmt FROM 'select * FROM users WHERE age > ?'; EXECUTE stmt using 18;
  • :name 要看驱动:PDO 支持 :age,但必须配合 prepare() + execute();直接丢进 mysql_query() 会报错或被当字面量处理
  • 别混用:一个语句里不能同时写 ?:id,驱动通常只认一种模式,混用大概率触发 SQLSTATE HY093(无效参数编号)错误

存储过程中 IN/OUT/INOUT 参数怎么真正生效?

这三个关键字不是装饰,它们决定了变量生命周期和调用方能否拿到值——OUT 参数在进入过程时自动初始化为 NULL,哪怕你传了个非空值进来,它也会被忽略;而 INOUT 才是“既读又写”的真实双向通道。

  • IN:只进不出,适合过滤条件,比如 CREATE PROCEDURE get_user(IN uid int) BEGIN SELECT * FROM users WHERE id = uid; END;
  • OUT:只出不进,调用前必须声明用户变量,比如 SET @count = 0; CALL count_users(18, @count); SELECT @count; —— 不声明 @count 就直接 CALL,过程执行完你也拿不到值
  • INOUT:值先传入再被修改,适合需要“原地更新”的场景,比如字符串拼接:SET @s = 'hello'; CALL append_world(@s); SELECT @s;,过程内必须有 SET s = CONCAT(s, ' world');

SQL Server 的 sp_executesql 为什么比 EXEC 更安全?

因为 EXEC('SELECT * FROM '+ @table) 是字符串拼接,@table 一旦含恶意内容(如 'users; DROP TABLE users--'),就直接执行注入语句;而 sp_executesql 强制把动态部分和参数分开,参数永远被当数据处理,不会进 SQL 解析器。

  • 正确写法:DECLARE @sql NVARCHAR(MAX) = N'SELECT * FROM users WHERE age > @age'; EXEC sp_executesql @sql, N'@age INT', @age = 25;
  • 错误写法:EXEC('SELECT * FROM users WHERE age > ' + CAST(@age AS VARCHAR)) —— 看似一样,实则失去参数化保护
  • 注意类型匹配:@age = 25 中的 25 是 int,如果传字符串却没加引号,会报 Conversion failed when converting the varchar value...

MyBatis 传多个参数时,@Parammap 哪个更稳?

@Param("uid") 显式标注参数名,xml 中才能写 #{uid};否则 MyBatis 3.4+ 默认按位置映射为 #{param1}#{param2},极易因方法重载或参数调整导致取错值。

  • 推荐 @Param:适合 2–5 个固定参数,比如 findUser(@Param("uid") Long uid, @Param("status") String status)
  • Map:适合参数数量不固定或需运行时组装,但必须确保 key 名和 XML 中一致,且 Map 本身不能为 null,否则抛 BindingException
  • 避免 JavaBean:虽然直观,但一旦接口要兼容旧字段+新字段,就得改实体类加 @JsonIgnore 或判空逻辑,反而增加维护成本

参数传递最常被忽略的一点:数据库驱动版本和 SQL 方言的隐式差异。比如 MySQL 8.0+ 的 PREPARE 对用户变量支持更严格,而低版本允许某些模糊写法;SQL Server 的 sp_executesqlazure SQL 中对参数长度限制也不同。上线前务必在目标环境验证参数绑定行为。

text=ZqhQzanResources