ora-01031因存储过程默认authid definer导致编译期忽略角色权限,需显式授予create any table等系统权限;改用authid current_user可启用运行时角色权限,但execute immediate执行select必须带into或open for,否则报ora-00900。

Oracle 在存储过程中执行 EXECUTE IMMEDIATE 报 ORA-01031: 权限不足,不是因为你没登录、没角色,而是权限“看不见”——它在编译期被静态检查,而角色权限(比如 DBA)默认不生效。
为什么匿名块能跑,存储过程就报错?
匿名块(BEGIN ... END;)是即时解析、即时执行的,Oracle 拿当前会话的有效权限去校验,所以你有 DBA 角色就能建表;但存储过程是“定义者权限(AUTHID DEFINER)”模式,默认开启——这意味着:编译时就锁定创建者的权限上下文,运行时只认直接授予该用户的系统权限,无视角色。
常见表现:
-
GRANT DBA TO scott;后,EXECUTE IMMEDIATE 'CREATE TABLE t(x INT)';在匿名块里成功,进存储过程就崩 -
SELECT * FROM session_roles;能看到DBA,但存储过程里查session_roles为空
EXECUTE IMMEDIATE 执行 DDL 必须显式授权
所有通过 EXECUTE IMMEDIATE 发出的 DDL(CREATE / ALTER / DROP 等),都受此限制。不能靠角色,必须用 GRANT 直接给用户授予权限:
- 建表 →
GRANT CREATE ANY TABLE TO scott; - 建视图 →
GRANT CREATE ANY VIEW TO scott; - 建序列 →
GRANT CREATE ANY SEQUENCE TO scott; - 操作其他用户的对象 →
GRANT SELECT ANY TABLE TO scott;(注意不是SELECT_CATALOG_ROLE)
这些权限必须由 SYS 或具备 ADMIN OPTION 的用户执行,且不能通过角色中转。
不想改权限?试试 AUTHID CURRENT_USER
把存储过程声明为调用者权限,就能绕过编译期的权限锁定,改在运行时动态检查调用者的会话权限(此时角色生效):
CREATE OR REPLACE PROCEDURE create_table_proc (tn VARCHAR2) AUTHID CURRENT_USER IS sqlstr VARCHAR2(200); BEGIN sqlstr := 'CREATE TABLE ' || tn || ' (id NUMBER)'; EXECUTE IMMEDIATE sqlstr; END;
但要注意:
- 调用者必须自己拥有对应权限(比如执行者得有
CREATE ANY TABLE或所在角色已启用) - 名称解析环境变成调用者 Schema,
EXECUTE IMMEDIATE 'SELECT * FROM emp'查的是调用者自己的emp表,不是定义者的 - 不能用于需要跨 Schema 稳定访问的场景(比如通用工具包)
为什么 SELECT 不报权限错,却报 ORA-00900?
这不是权限问题,是语法错误:EXECUTE IMMEDIATE 执行 SELECT 必须带接收目标,否则 Oracle 认为语句不完整:
- 单行 → 必须加
INTO变量:EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM dept' INTO v_cnt; - 多行 → 必须用
OPEN ... FOR绑定REF CURSOR:OPEN rc FOR 'SELECT * FROM dept'; - 裸写
EXECUTE IMMEDIATE 'SELECT ...';一定会触发ORA-00900: invalid SQL statement
这个限制和权限无关,也和 AUTHID 无关,纯粹是 Oracle 动态 SQL 的语义要求——结果集不能悬空。
真正容易被忽略的点是:权限缺失和语法错误两种 ORA- 错误常被混为一谈;但前者看 dba_sys_privs 和 session_roles 对比,后者看语句有没有 INTO 或游标绑定。别一见报错就急着 GRANT,先确认是不是根本没写接收逻辑。











