在CDB$ROOT中创建以C##开头的公用用户必须显式指定CONTAINER=ALL,否则报ORA-65094;用户名须大写、不加引号;common_user_prefix为只读参数,不可修改;登录PDB需确认当前容器并显式授权解锁。

CREATE USER 语句必须显式写 CONTAINER = ALL
在 CDB$ROOT 中执行 CREATE USER 创建以 C## 开头的用户时,光有前缀还不够。漏掉 CONTAINER = ALL 就会触发 ORA-65094(本地用户不允许在根容器中创建),而不是你预想的 ORA-65096。Oracle 默认把没指定 CONTAINER 的语句当作 CONTAINER = CURRENT 处理,而 CDB$ROOT 不允许本地用户存在。
正确写法只有一条路径:
CREATE USER c##admin IDENTIFIED BY pwd CONTAINER = ALL;- 不能省略
CONTAINER = ALL,哪怕你确认当前就是CDB$ROOT - 不能用双引号包裹用户名,如
"c##admin"—— 这会绕过命名校验但导致后续权限、对象访问异常 - 创建后立即验证:运行
SELECT username, con_id FROM cdb_users WHERE username = 'C##ADMIN';,结果应包含多个CON_ID(至少CDB$ROOT和每个OPEN状态的PDB)
为什么 common_user_prefix 参数改不了这个行为?
common_user_prefix 是个只读参数,且仅影响字典视图和部分内部逻辑,它不参与 SQL 解析阶段的用户名校验。真正起作用的是硬编码在 ksuCheckCommonUserName 函数里的正则:^C##|^c## —— 大小写敏感、两个井号必须连续开头、不接受空格或下划线。改参数值既不会让 app_user 通过校验,也不会让 c##user(小写 c)合法。
实操建议:
- 别动
common_user_prefix,Oracle 官方文档明确说“修改该参数不被支持” - 如果真需要自定义前缀(比如用
MY##),必须在建库时通过 DBCA 或手动脚本指定,19c 运行时无法切换 - 所有 DBA 脚本中涉及公用用户创建,都应统一用大写
C##+CONTAINER = ALL模式,避免环境迁移时出错
tnsnames.ora 配置错误会导致你以为切到了 PDB
很多人执行了 ALTER SESSION SET CONTAINER = mypdb; 后就以为接下来的 CREATE USER 一定在 PDB 里生效,其实不然。SQL Developer、Navicat 等工具新建查询窗口默认连的是 CDB service(如 ORCL),ALTER SESSION 只对当前会话有效,新开窗口又回到 CDB$ROOT。更隐蔽的是:tnsnames.ora 里如果没为 PDB 单独配 service,你用 conn / as sysdba 登录后,即使切了 container,后台连接串仍可能指向 CDB。
必须做两件事:
- 在
tnsnames.ora中添加一条独立的 PDB 条目,例如:MYDB = (DESCRIPTION = (SERVICE_NAME = mypdb)) - 连接时显式使用该别名:
conn system@mydb,而不是conn system@orcl - 每次操作前确认:
SELECT SYS_CONTEXT('USERENV', 'CON_NAME') FROM DUAL;返回值必须是你的目标 PDB 名,不是CDB$ROOT
公用用户不能直接登录 PDB,除非授权并解锁
即使 c##admin 在所有容器中都存在,它默认只能在 CDB$ROOT 登录。想让它在某个 PDB 中可用,得额外做三件事:
- 在目标 PDB 中执行:
GRANT CREATE SESSION TO c##admin; - 确保该用户在该 PDB 中未被锁定:
ALTER USER c##admin ACCOUNT UNLOCK; - 如果 PDB 中启用了密码复杂度策略(如
VERIFY_FUNCTION),而c##admin的密码不符合要求,CREATE USER虽成功,但登录时仍报ORA-01017
最易忽略的一点:公用用户的密码在所有容器中是同步的,但账户状态(ACCOUNT STATUS)是按容器独立维护的。一个 PDB 中解锁了,不代表另一个 PDB 也解锁了。


















