必须分四类调用dbms_metadata.get_granted_ddl:system_grant、role_grant、default_role、object_grant,且用户名大写、set long足够大;还需单独处理tablespace_quota、profile和user定义,并按建用户→设profile→配配额→授权限顺序执行。
直接用 dbms_metadata.get_granted_ddl 就能拿到可执行的权限脚本,但必须分四类调用、用户名大写、set long 要设够大,否则输出截断或为空——这不是“差不多能用”,而是漏一条就可能让新用户连不上库。
为什么只跑一次 GET_GRANTED_DDL('OBJECT_GRANT', 'U1') 不够
对象权限只是冰山一角。一个能正常登录、建表、查数据的用户,至少依赖三类权限:对象权限(如 GRANT SELECT ON EMP TO U1)、角色权限(如 GRANT CONNECT TO U1)、系统权限(如 GRANT CREATE SESSION TO U1)。还有一类常被忽略:默认角色(ALTER USER U1 DEFAULT ROLE CONNECT),它决定连接后哪些角色自动生效。
如果只取 OBJECT_GRANT,新用户即使有表权限,也可能因缺 CREATE SESSION 而无法登录,或因没设默认角色而连上后查不了任何视图。
必须分别执行这四条:
SELECT DBMS_METADATA.GET_GRANTED_DDL('SYSTEM_GRANT', 'U1') FROM DUAL;SELECT DBMS_METADATA.GET_GRANTED_DDL('ROLE_GRANT', 'U1') FROM DUAL;SELECT DBMS_METADATA.GET_GRANTED_DDL('DEFAULT_ROLE', 'U1') FROM DUAL;SELECT DBMS_METADATA.GET_GRANTED_DDL('OBJECT_GRANT', 'U1') FROM DUAL;
注意:U1 必须全大写;若用小写或带引号,函数返回空 CLOB,不是报错,容易误判为“没权限”。
SET LONG 设太小会导致权限语句被截断
DBMS_METADATA.GET_GRANTED_DDL 返回的是 CLOB,SQL*Plus 默认 LONG 是 80 字符,远不够容纳多条 GRANT 语句。一旦截断,生成的脚本里 GRANT 缺半句,执行时报 ORA-00922: missing or invalid option。
必须在执行前显式设置:
SET LONG 100000 SET PAGESIZE 0 SET FEEDBACK OFF SET TRIMSPOOL ON
不设 PAGESIZE 0 可能混入页眉页脚;不关 FEEDBACK 会在输出里插 “1 row selected”,污染脚本。
验证是否生效:执行后看第一行是不是完整的 GRANT,而不是以 GRANT SELECT ON ... 开头、后面跟着一堆省略号。
同义词和系统对象权限会引发 ORA-00942
GET_GRANTED_DDL('OBJECT_GRANT', 'U1') 遇到通过同义词授权的情况(比如 GRANT SELECT ON MY_EMP TO U1,而 MY_EMP 是 SCOTT.EMP 的同义词),它原样输出同义词名,不会解析成真实对象。如果目标库没建这个同义词,或同义词指向的对象不存在,执行时直接报 ORA-00942: table or view does not exist。
更危险的是对 SYS 或 SYSTEM 下视图的授权(如 GRANT SELECT ON DBA_TABLES TO U1)。这类权限在克隆用户时通常不该照搬——新环境未必开了 SELECT_CATALOG_ROLE,且暴露系统视图有安全风险。
建议在导出后手动过滤掉这些行,或提前加条件:
- 检查
DBA_TAB_PRIVS中OWNER IN ('SYS', 'SYSTEM')的记录,评估是否真要迁移 - 对含
MY_EMP这类同义词的 GRANT,确认目标库已存在同义词,或改写为真实对象名(如SCOTT.EMP)
表空间配额和 PROFILE 必须单独处理
DBMS_METADATA.GET_GRANTED_DDL 不覆盖表空间配额(QUOTA)和用户配置文件(PROFILE)。如果新用户没配额,执行 CREATE TABLE 会报 ORA-01950: no privileges on tablespace 'USERS';如果没指定 PROFILE,可能受默认口令策略限制(比如 180 天过期)。
这两项需额外补上:
- 表空间配额:
SELECT DBMS_METADATA.GET_GRANTED_DDL('TABLESPACE_QUOTA', 'U1') FROM DBA_USERS WHERE USERNAME = 'U1'; - PROFILE 定义:
SELECT DBMS_METADATA.GET_DDL('PROFILE', p.profile) FROM DBA_PROFILES p WHERE p.profile IN (SELECT profile FROM dba_users WHERE username = 'U1'); - 用户定义本身(含密码哈希):
SELECT DBMS_METADATA.GET_DDL('USER', 'U1') FROM DUAL;—— 注意该语句含加密密码,可直接用于重建用户
真正麻烦的不是生成脚本,而是执行顺序:必须先建用户、再设 PROFILE、再配表空间、最后授各类权限。顺序错一步,后续 GRANT 就会因用户不存在或无配额而失败。











