SELECT ANY TABLE 不能安全实现只读用户目标,因其在ADG备库必报ORA-01031、无法按schema收回、权限失控,应改用显式GRANT SELECT ON具体对象并封装角色。
select any table 不能安全实现只读用户目标——它不是可控方案,而是权限失控的起点。生产环境直接用它配只读账号,等于给用户开了全库表的后门,且在 adg 备库上必然失效。
为什么SELECT ANY TABLE在ADG备库上一定报ORA-01031
ADG物理备库处于 READ ONLY WITH APPLY 状态,所有数据字典表(如 OBJ$、USER$)强制只读。SELECT ANY TABLE 内部依赖对这些字典表的隐式访问(含部分写逻辑),Oracle 主动禁用该权限来保一致性。即使 GRANT SELECT ANY TABLE TO ro_user; 执行成功,后续任何 SELECT 都会触发 ORA-01031: insufficient privileges。
- 这不是缓存没刷、不是授权延迟,是 Oracle 的硬性限制
-
SELECT ANY DICTIONARY同样无效,备库不开放“任意读字典”的通道 - 哪怕你用
SYS登录执行授权,结果也一样
用SELECT ANY TABLE授出的权限根本没法按 schema 收回
它不绑定 schema,也不区分对象类型。一旦执行 GRANT SELECT ANY TABLE TO app_ro;,用户就能查 HR.EMPLOYEES、SCOTT.DEPT,甚至未来新建的任意普通表——完全失控。
- 想限制为只读
APP_SCHEMA?做不到。没有REVOKE SELECT ANY TABLE ON APP_SCHEMA.*这种语法 - 只能整权收回:
REVOKE SELECT ANY TABLE FROM app_ro;,或手动逐表REVOKE SELECT ON ...,维护成本爆炸 - 若误加
WITH ADMIN OPTION(虽然语法上不支持SELECT ANY TABLE加该选项,但类似权限如SELECT_CATALOG_ROLE可以),权限链更难追踪
真正能落地的只读账号必须基于显式对象授权
核心是放弃 “ANY” 思维,转向 GRANT SELECT ON schema.table TO user + 角色封装。前提是目标 schema 已同步到备库:
- 先确认表存在:
SELECT COUNT(*) FROM dba_tables WHERE owner = 'CMSPROD';,若返回 0,说明主库未传输元数据 - 生成批量授权语句(在备库上执行):
SELECT 'GRANT SELECT ON CMSPROD.' || table_name || ' TO cmsreadonly;' FROM dba_tables WHERE owner = 'CMSPROD' AND temporary = 'N'; - 物化视图要单独处理:
SELECT 'GRANT SELECT ON CMSPROD.' || mview_name || ' TO cmsreadonly;' FROM dba_mviews WHERE owner = 'CMSPROD'; - 视图、同义词不继承权限,必须对底层对象单独授权;动态性能视图(如
v$session)需额外角色或自定义视图
基础权限别漏:CREATE SESSION 和账号锁定
只读角色只管数据访问,用户连不上库等于白搭:
- 必须显式授予:
GRANT CREATE SESSION TO readonly_user;(别用CONNECT角色,旧版本可能附带UNLIMITED TABLESPACE) - 生产环境应禁用交互登录:
ALTER USER readonly_user ACCOUNT LOCK;,需要时再UNLOCK - 设
QUOTA 0 ON users,防止某些客户端工具(如 PL/SQL Developer)触发隐式临时段创建 - 验证是否真只读:
SELECT * FROM v$session应报ORA-00942;CREATE TABLE t AS SELECT ...应报ORA-01031
复杂点不在授权语句本身,而在 schema 同步状态、对象类型覆盖(表/视图/物化视图/同义词)、以及多租户环境下角色是否在正确 PDB 中创建——这些地方一漏,权限就断在看不见的地方。


















