EXECUTE IMMEDIATE必须用USING传参,严禁拼接用户输入;绑定变量仅适用于值,不适用于表名、列名等结构;动态对象名须经DBMS_ASSERT校验或白名单控制。

EXECUTE IMMEDIATE必须用USING传参,不能拼字符串
只要用了EXECUTE IMMEDIATE,又把用户输入或变量直接拼进SQL字符串里,就等于开了SQL注入后门。Oracle不校验拼出来的字符串是否安全,只管语法对不对——' or 1=1 --这种输入一拼进去,整条语句就失控。
安全的唯一路径是让参数完全脱离SQL字符串上下文,靠USING子句传递:
-
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE deptno = :1' INTO v_name USING dept_id;✅ 正确:占位符:1是语法标记,值由USING单独提供 -
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE deptno = ' || dept_id;❌ 危险:字符串拼接,无任何防护 -
EXECUTE IMMEDIATE 'SELECT * FROM emp WHERE deptno = :1' USING dept_id;✅ 合法,哪怕没INTO也安全(比如DML)
多个参数时顺序和类型必须严格匹配
USING后的变量顺序,必须和SQL中占位符出现的顺序完全一致;而且每个变量类型要能被Oracle隐式转换为对应列类型,否则可能走错执行计划甚至报错。
常见踩坑点:
- 写成
USING v2, v1但SQL里是WHERE a = :1 AND b = :2→ 值传反,查不到数据或逻辑错误 - 用
VARCHAR2变量绑定NUMBER字段(如deptno = :1),Oracle会自动转,但可能触发隐式转换,导致索引失效 -
USING UPPER(name)非法:表达式不能直接放USING后,得先v_name := UPPER(name);再USING v_name - IN/OUT/IN OUT模式要显式声明,比如调用存储过程:
USING IN v_table, OUT v_cnt, IN OUT v_status
RETURNING INTO和BULK COLLECT配合绑定变量的写法
DML语句想取回刚修改的数据(比如插入后拿到自增ID、删除后拿到被删姓名),要用RETURNING INTO;批量操作则用BULK COLLECT INTO,它们都支持绑定变量,但语法位置固定。
关键规则:
-
RETURNING必须紧跟在DML语句之后,INTO变量类型要和返回字段匹配:DELETE FROM emp WHERE empno = :1 RETURNING ename INTO v_ename USING 7369; - 批量查询不能用普通
INTO,必须用BULK COLLECT INTO:EXECUTE IMMEDIATE 'SELECT ename FROM emp WHERE deptno = :1' BULK COLLECT INTO v_names USING 20; -
FORALL批量DML也依赖绑定变量:FORALL i IN 1..v_ids.COUNT EXECUTE IMMEDIATE 'UPDATE emp SET sal = :1 WHERE empno = :2' USING v_new_sal(i), v_ids(i);
表名、列名、排序字段不能用绑定变量
绑定变量只适用于**值(value)**,不适用于**结构(structure)**。这意味着:table_name或:order_col这种写法语法就报错——Oracle解析器根本不会把它们当占位符处理。
真要动态对象名,只能拼字符串,但必须极度谨慎:
- 用
DBMS_ASSERT.SQL_OBJECT_NAME校验表名:v_safe_table := DBMS_ASSERT.SQL_OBJECT_NAME(v_user_input);,再拼'SELECT * FROM ' || v_safe_table - 列名、ORDER BY字段同理,没有通用安全函数,只能白名单控制或应用层预设枚举
- IN子句不能写
WHERE id IN (:1, :2)然后绑两个值——占位符数量必须固定;正确做法是用集合类型或临时表
最易被忽略的是:哪怕你100%用了USING,只要SQL字符串里混进了任何用户可控的结构部分,整个语句就不再可信。安全不是“用了绑定变量”,而是“所有动态部分都经过隔离与校验”。


















