DBMS_METADATA.GET_GRANTED_DDL仅支持对象权限导出,无法获取系统权限、角色、用户属性等;需联合查询DBA_SYS_PRIVS、DBA_ROLE_PRIVS、DBA_TAB_PRIVS等视图并手动拼装完整DDL脚本。

DBMS_METADATA.GET_GRANTED_DDL 只能导出对象权限,别指望它搞定全部
很多人一上来就写 SELECT DBMS_METADATA.GET_GRANTED_DDL('TABLE', 'EMP', 'SCOTT') FROM DUAL,以为这样就能把 SCOTT 的权限全捞出来——其实这只返回一条 GRANT SELECT ON SCOTT.EMP TO XXX 这类语句。它根本不会碰系统权限(比如 CREATE SESSION)、角色授予(比如 GRANT DBA TO SCOTT)、默认表空间、密码策略,甚至连列级权限都不包含。
更关键的是:DBMS_METADATA.GET_GRANTED_DDL('SYSTEM_GRANT', ...) 直接报 ORA-31600: invalid object type SYSTEM_GRANT,函数根本不支持这个类型。
所以,想靠单条 SQL 或一个函数“一键备份用户权限”,这条路走不通。
真正要克隆完整权限,得查多个数据字典视图拼 SQL
你需要组合查询以下视图,再人工或脚本转成可执行的 DDL:
-
SELECT * FROM DBA_SYS_PRIVS WHERE GRANTEE = 'SCOTT'→ 拿系统权限(CREATE TABLE、UNLIMITED TABLESPACE等) -
SELECT * FROM DBA_ROLE_PRIVS WHERE GRANTEE = 'SCOTT'→ 拿授予的角色(CONNECT、RESOURCE、自定义角色) -
SELECT * FROM DBA_TAB_PRIVS WHERE GRANTEE = 'SCOTT'→ 拿对象权限(注意:不含列级权限,需额外查DBA_COL_PRIVS) -
SELECT USERNAME, DEFAULT_TABLESPACE, TEMPORARY_TABLESPACE, ACCOUNT_STATUS, EXPIRY_DATE FROM DBA_USERS WHERE USERNAME = 'SCOTT'→ 拿用户基础属性 -
SELECT * FROM DBA_PROFILES WHERE PROFILE IN (SELECT PROFILE FROM DBA_USERS WHERE USERNAME = 'SCOTT')→ 拿密码策略(如 FAILED_LOGIN_ATTEMPTS)
这些结果不是现成的 SQL,需要你写脚本或手动补上 GRANT、ALTER USER、CREATE USER 语句。例如:CREATE USER scott IDENTIFIED BY VALUES '...' DEFAULT TABLESPACE users TEMPORARY TABLESPACE temp;
expdp/impdp 默认不导出用户定义,容易踩坑
用 expdp 导出时,即使加了 SCHEMAS=SCOTT,默认也不会导出 CREATE USER 语句和系统权限——除非显式加上 INCLUDE=USER 和 GRANTS=Y。
但即便如此,仍缺关键项:
- 用户密码哈希值(
IDENTIFIED BY VALUES)可能因版本或 wallet 配置无法还原登录能力 - 密码策略、账户锁定状态、profile 关联等不会被
expdp自动重建 - 如果目标库已存在同名用户,
impdp不会覆盖其权限,而是跳过或报错ORA-39083: Object type USER failed to create
所以不能依赖导出文件“顺带”解决权限问题,必须前置处理用户创建与基础属性同步。
克隆权限前,先确认目标用户状态是否干净
目标用户(比如 SCOTT2)如果已存在,它的当前权限、角色、profile、默认表空间都可能干扰导入结果。最稳妥的做法是:
- 先
DROP USER scott2 CASCADE彻底清空 - 再用你拼好的完整建用户 + 授权脚本重建
- 避免用
ALTER USER增量修改,因为部分权限(如DEFAULT ROLE)在 ALTER 中不生效,必须ALTER USER ... DEFAULT ROLE ALL显式指定
特别注意大小写:如果源用户是 "ScOtt"(双引号创建),目标也必须用双引号,否则 DBA_USERS.USERNAME 查不到,后续所有权限查询都会漏掉。
权限克隆最难的不是查哪些视图,而是把零散结果组装成一套无冲突、可重复执行、能真实还原登录与操作能力的 SQL 脚本——中间任何一步缺失,新用户都可能连 SELECT * FROM DUAL 都执行不了。


















