sp_executesql比EXEC更安全高效,因其支持参数化使SQL Server可复用执行计划;EXEC拼接字符串会强制重编译且易受注入攻击。

SQL Server 存储过程里为什么 sp_executesql 比 EXEC 更安全高效
因为 sp_executesql 支持参数化,SQL Server 能复用执行计划;而 EXEC 拼接字符串会强制重新编译,还容易被注入。
常见错误现象:EXEC('SELECT * FROM users WHERE id = ' + @id) 看似省事,但 @id 是字符串时直接报错,是数字时又可能被注入——比如传入 '1 OR 1=1' 就全表泄露。
- 必须把变量声明为
@param_name datatype,再在 SQL 字符串里用@param_name占位 - 参数定义字符串(第二个参数)必须是字面量,不能是变量,否则失去预编译优势
- 类型要严格匹配:如果列是
INT,传@id NVARCHAR(10)可能触发隐式转换,导致索引失效
DECLARE @sql NVARCHAR(MAX) = N'SELECT name FROM users WHERE status = @status AND created_at > @since'; EXEC sp_executesql @sql, N'@status TINYINT, @since DATETIME2', @status = 1, @since = '2024-01-01';
MySQL 存储过程中没法用 PREPARE + EXECUTE 做参数化?其实是能的,但写法很关键
MySQL 不支持像 SQL Server 那样直接传参数进动态 SQL 字符串,必须用 PREPARE 绑定用户变量(@var),且这些变量必须在 EXECUTE 前已赋值。
使用场景:分表查询、按字段名动态排序、WHERE 条件可选组合。
-
SET @status = 1;必须在PREPARE之前完成,不能写成PREPARE stmt FROM CONCAT(...)里拼进去 - 所有参数都要用
@开头的用户变量,不能用存储过程的局部变量(如v_status) - 每次
EXECUTE前最好DEALLOCATE PREPARE,否则高并发下可能报MySQL Error 1243: Unknown prepared statement handler
SET @status = 1; SET @sql = 'SELECT name FROM users WHERE status = ?'; PREPARE stmt FROM @sql; EXECUTE stmt USING @status; DEALLOCATE PREPARE stmt;
PostgreSQL 的 EXECUTE ... USING 容易漏掉类型推导,导致隐式转换失败
PostgreSQL 动态 SQL 中,USING 后的参数默认按上下文推断类型,如果目标字段是 UUID 或 NUMERIC(10,2),传普通字符串或 float 可能报错或精度丢失。
性能影响:类型不匹配时,PostgreSQL 无法复用执行计划,每次都会生成新计划,pg_prepared_statements 里能看到重复条目。
- 显式转类型最稳:用
USING p_id::UUID, p_amount::NUMERIC - 避免在
EXECUTE字符串里拼接条件逻辑,比如'AND status = ' || p_status—— 这就退化成字符串拼接,失去参数化意义 - 如果参数来自
RECORD字段,先赋值给带类型的变量再传,别直接USING rec.id
Oracle PL/SQL 里 EXECUTE IMMEDIATE 的绑定变量数量限制和空值陷阱
Oracle 允许最多 32767 个绑定变量,但实际中超过几百个就容易触发 ORA-01000: maximum open cursors exceeded,尤其在循环里反复 EXECUTE IMMEDIATE 却没释放。
最容易被忽略的是 NULL:绑定变量传 NULL 时,Oracle 默认按 VARCHAR2 推断,如果目标字段是 NUMBER 或 DATE,就会报 ORA-06502。
- 显式声明变量类型,比如
l_id NUMBER := NULL;,再传入USING l_id - 批量操作优先用
BULK COLLECT+FORALL,而不是循环调用EXECUTE IMMEDIATE - 调试时加
DBMS_OUTPUT.PUT_LINE打印实际 SQL 字符串,确认占位符和参数顺序完全对齐
参数化不是加几个 @ 或 ? 就完事,核心是让数据库引擎真正识别出“结构不变、仅值变化”,这要求类型一致、变量作用域正确、执行上下文稳定。稍有偏差,预编译就失效,甚至比普通查询更慢。

















