公用用户需在目标PDB内单独授权且会话必须切换至该PDB才能访问其对象;跨PDB访问不可通过ANY权限实现,仅支持DBLINK或PROXY PDB。

公用用户在CDB$ROOT中创建,但对象在PDB里
公用用户(c##开头 + CONTAINER=ALL)存在于CDB所有容器中,包括CDB$ROOT和每个PDB,但它默认没有对任何PDB内业务对象的访问权限。PDB的数据字典是独立的,hr.employees这类对象只注册在它所属的PDB里,CDB$ROOT中查不到这张表,公用用户自然也“看不见”。执行 SELECT * FROM hr.employees 会直接报 ORA-00942: table or view does not exist,不是权限问题,是对象根本不可见。
GRANT必须在目标PDB中执行,且不能带CONTAINER=ALL
给公用用户授PDB内对象权限,必须先切换到那个PDB,再执行授权语句。比如要让 c##reporter 能查 PDB1 中的 scott.emp:
ALTER SESSION SET CONTAINER = PDB1; GRANT SELECT ON scott.emp TO c##reporter;
注意三点:
-
GRANT语句不能加CONTAINER=ALL——这会导致权限只生效于CDB$ROOT,对PDB无效 - 必须在
PDB1内执行,不能在CDB$ROOT里用GRANT ... TO c##reporter@PDB1这种语法(Oracle不支持) - 如果目标对象属于本地用户(如
scott),该用户必须已在PDB1中存在且未过期/锁定
公用用户登录后默认在CDB$ROOT,需手动切容器才能访问PDB对象
即使你已成功授予了权限,c##reporter 用 sqlplus c##reporter/pwd@cdb_service 登录后,SYS_CONTEXT('USERENV', 'CON_NAME') 返回的仍是 CDB$ROOT。此时它能查 CDB$ROOT 的视图(如 DBA_PDBS),但查不了 PDB1 的表——因为会话没进PDB。
解决方法只有两个:
- 登录时直接连PDB服务名:
sqlplus c##reporter/pwd@pdb1_service(前提是tnsnames.ora里配置了指向PDB的服务) - 登录到CDB后手动切换:
ALTER SESSION SET CONTAINER = PDB1;
漏掉这步,前面所有授权都白做。
跨PDB对象访问不是“授权问题”,而是架构边界问题
很多人误以为“加个ANY权限就能穿透PDB”,但Oracle 19c明确禁止这种行为。SELECT ANY TABLE 只能在当前容器内生效;EXECUTE ANY PROCEDURE 同理。PDB之间的隔离是硬性设计,不是靠权限开关能绕过的。真正需要跨PDB读数据,只能走 DBLINK 或 PROXY PDB——前者是显式、可控的,后者是透明但配置复杂。想用公用用户“一账号通查所有PDB业务表”,这条路在19c里根本不存在。
最容易被忽略的是:权限、对象、会话容器,这三个要素必须严格对齐在同一PDB内,缺一不可。随便换一个环节在CDB$ROOT里操作,就全失效。











