在CDB$ROOT中创建c##前缀公用用户必须显式指定CONTAINER=ALL,否则仅存在于根容器;用户名须小写c##开头、不可引号包裹、不能为保留字;创建后需逐个PDB单独授权CREATE SESSION方可登录。

CREATE USER必须在CDB$ROOT中执行
如果当前连接的是某个PDB(比如HR_PDB),直接运行CREATE USER c##app IDENTIFIED BY pwd;会报ORA-65094: invalid local user or role name。Oracle不允许在PDB里创建以c##开头的用户——那会被当成“试图伪造公共用户”。
实操前先确认容器:SELECT SYS_CONTEXT('USERENV', 'CON_NAME') FROM DUAL;结果必须是CDB$ROOT。如果不是,用ALTER SESSION SET CONTAINER = CDB$ROOT;切换,或重新用sqlplus / as sysdba连CDB根。
c##前缀大小写敏感且不可省略
c##必须小写,C##虽被文档列为合法,但实际测试中部分版本(如12.1.0.2)会拒绝C##ADMIN,只认c##admin。双引号包裹(如"c##user")完全无效,Oracle解析阶段就报错。
用户名还不能是Oracle保留字,比如c##select或c##order会触发ORA-00922: missing or invalid option。建议用SELECT NAME FROM V$RESERVED_WORDS WHERE UPPER(NAME) = UPPER('c##xxx')提前检查。
CONTAINER = ALL不是可选项,而是作用域关键
即使用户名合规,漏写CONTAINER = ALL会导致用户只存在于CDB$ROOT,所有PDB里查不到该用户(SELECT USERNAME FROM CDB_USERS WHERE USERNAME = 'C##APP'在PDB中返回空)。这不是权限问题,是对象根本没广播过去。
正确写法只有这一种:CREATE USER c##app IDENTIFIED BY pwd CONTAINER = ALL;
-
CONTAINER = CURRENT在CDB$ROOT中禁止使用,会报ORA-65094 - 不写
CONTAINER子句时,Oracle默认按CONTAINER = ALL处理——但显式写出更安全,避免版本差异导致行为漂移 - 密码策略、默认表空间等参数和其他用户一样设置,无特殊限制
用户建完不等于能登录PDB
刚创建的c##app在PDB里无法直接CONNECT,因为CREATE SESSION权限默认只授予CDB$ROOT。必须逐个PDB授权:
ALTER SESSION SET CONTAINER = HR_PDB;GRANT CREATE SESSION TO c##app;
注意:GRANT CONNECT, RESOURCE TO c##app CONTAINER = ALL在CDB$ROOT中执行是无效的——CONTAINER = ALL只对DML类权限(如SELECT ANY TABLE)生效,对角色授权不广播。每个PDB的权限必须单独进容器授予。
真正容易被忽略的一点:公共用户在不同PDB中可以拥有完全不同的schema对象、同义词甚至同名但结构不同的表。别假设c##app在PDB A和PDB B里的EMP表是一回事——它们物理隔离,DDL操作互不影响。











