EXECUTE IMMEDIATE 不支持对表名、列名等标识符使用绑定变量,因解析阶段需确定对象结构而绑定值仅执行时才代入;必须通过 DBMS_ASSERT.SIMPLE_SQL_NAME 等校验后拼接,且 DDL 自动提交不可回滚,跨环境需显式指定 Schema 并校验权限。
EXECUTE IMMEDIATE 不能对表名、列名等标识符使用绑定变量,这是硬性限制,不是写法问题 —— 直接用 :table_name 肯定报 ORA-00184 或 ORA-00900。
Oracle 在 SQL 解析阶段就必须确定对象结构(比如表是否存在、字段类型是否匹配),而绑定变量的值只在执行时才代入,解析器无法据此做元数据校验。所以 DDL 和 DML 中所有对象名(CREATE TABLE :t、SELECT * FROM :tab)一律不支持绑定。
直接字符串拼接 v_sql := 'SELECT * FROM ' || v_tabname 看似简单,但若 v_tabname 来自用户输入或外部参数,极易引发 SQL 注入 —— 比如传入 't1 UNION SELECT password FROM users--' 就可能拖库。
DBMS_ASSERT.SIMPLE_SQL_NAME 校验:它只允许字母、数字、下划线、井号、美元符,且长度 ≤ 30,能拦掉绝大多数恶意输入REGEXP_LIKE(v_tabname, '^[a-zA-Z][a-zA-Z0-9_-#$]{0,29}$')
REPLACE 或 TRANSLATE 做“过滤”,它们无法覆盖嵌套注入(如 't1 --' 后加换行)用 EXECUTE IMMEDIATE 执行 CREATE、DROP 等 DDL,事务会立即提交,哪怕外面包着 BEGIN...EXCEPTION...END 也无效。这意味着:
DBMS_SQL 包(但性能差、代码冗长,仅限极特殊场景)SELECT COUNT(*) FROM user_tables WHERE table_name = UPPER(v_tabname)),避免重复建表;删除前先 TRUNCATE 再 DROP,减少 DDL 失败风险拼接表名时只写 v_tabname,上线到其他环境可能报 ORA-00942(表或视图不存在)—— 因为开发库默认在当前 Schema 下查,而生产库可能要求显式指定 Schema,比如 'SCOTT.' || v_tabname。
USER 视图查当前用户下的对象,避免硬编码 SchemaDBMS_ASSERT.ENQUOTE_NAME 安全校验真正麻烦的不是怎么拼,而是拼完之后谁来担保它安全、可回滚、跨环境可用 —— 这些点不提前卡住,上线后修起来比重写还费劲。