必须用动态sql逐条执行授权语句,因oracle pl/sql不支持静态grant;需分别查询系统权限、对象权限(表/视图、列、序列)及角色,并处理大小写、双引号、with option选项;每条语句须execute immediate加异常捕获,且应校验对象存在性与最小权限原则。
不能直接“同步权限”,必须用动态 sql 逐条生成并执行授权语句。 oracle 不允许在 pl/sql 块里写静态 grant,硬写会报 pls-00103;所谓“同步”,本质是查出源用户的权限,再对目标用户重放一遍——但要注意对象存在性、大小写、特殊字符和权限类型差异。
查哪些权限:区分系统权限和对象权限
系统权限(如 CREATE SESSION)和对象权限(如 SELECT ON HR.EMPLOYEES)来源不同,必须分开查:
- 系统权限从
DBA_SYS_PRIVS查,过滤GRANTEE = 'SOURCE_USER' - 对象权限从
DBA_TAB_PRIVS(表/视图)、DBA_COL_PRIVS(列级)、DBA_SEQ_PRIVS(序列)等分别查,注意OWNER和TABLE_NAME字段 - 别漏掉角色:查
DBA_ROLE_PRIVS获取源用户被授予的角色,这些也要转授给目标用户 -
WITH ADMIN OPTION和WITH GRANT OPTION需单独判断,不建议无差别复制,容易放大权限风险
拼 SQL 时必须处理双引号和大小写
Oracle 默认把未加引号的标识符转成大写。如果源用户有带小写或连字符的对象名(比如 "my_table" 或 "Log-Date"),不加双引号就会找错对象:
- 所有
OWNER、TABLE_NAME、COLUMN_NAME、GRANTEE值,只要可能含小写或特殊字符,就用双引号包裹,例如:'GRANT SELECT ON "' || owner || '"."' || table_name || '" TO "TARGET_USER";' - 检查
DBA_USERS中用户名是否为大写;若目标用户是"app_dev"(小写创建),TO app_dev会失败,必须写成TO "app_dev" - 用
UPPER()或LOWER()统一转换前,先确认原始值是否真被存为小写——查DBA_USERS.username字段本身内容
执行时用 EXECUTE IMMEDIATE + 异常捕获
每条授权语句必须单独 EXECUTE IMMEDIATE,且不能跳过失败项,否则整个块中断:
- 不要拼接多条
GRANT用分号连起来——Oracle 不支持 - 必须用游标循环 +
BEGIN ... EXCEPTION WHEN OTHERS THEN NULL; END;包裹每条执行,否则一张表不存在或权限不足,后续全停 - 加日志输出:用
DBMS_OUTPUT.PUT_LINE('Granting: ' || v_sql);确认实际执行内容 - 测试阶段先用
DBMS_OUTPUT.PUT_LINE(v_sql);替代EXECUTE IMMEDIATE,人工核对生成的语句是否合理
真正难的是“该不该授”而不是“怎么授”
脚本跑通不等于权限设计合理。生产环境最容易忽略三点:
- 源用户可能有已失效的对象权限(比如原表被删但权限记录还在),查
DBA_TAB_PRIVS时不校验对象是否存在,会导致大量ORA-00942错误 - 系统权限如
UNLIMITED TABLESPACE或DBA角色,绝不能盲目复制——应按最小权限原则手工筛选 - 目标用户若尚未创建,脚本会直接报错;建议前置检查
SELECT COUNT(*) FROM DBA_USERS WHERE USERNAME = 'TARGET_USER',没则提前CREATE USER











