oracle字符集不可直接alter database characterset修改,仅支持升级(如zhs16gbk→al32utf8),降级或跨编码族转换必须通过expdp/impdp导出导入或已弃用的csscan+csalter;生产操作需停业务、全库备份,并严格匹配nls_lang与源/目标库字符集,否则导致不可逆乱码;clob、nchar等类型易截断或解码异常,须人工校验;12c+必须用dbms_datapump及version=12参数,避免使用过时exp/imp。
oracle 字符集不能直接 alter database character set 修改
想把 al32utf8 改成 zhs16gbk(或反过来),用 alter database character set 命令会报错 ora-12712: new character set must be a superset of the old character set。因为 oracle 只允许升级(如 zhs16gbk → al32utf8),不支持降级或跨编码族转换。
真正可行的路径只有两种:导出再导入(expdp/impdp),或使用内部工具 csscan + csalter(仅限部分兼容场景,且 12c 起已弃用)。生产库务必先停业务、备份全库。
-
csscan只能用于ZHS16GBK→AL32UTF8的预检,不能反向扫描 -
csalter脚本在 12.2+ 默认禁用,需设置_allow_resetlogs_corruption=TRUE等隐含参数,风险极高 - 即使字符集“看似兼容”,中文乱码往往出现在 CLOB、NCHAR 类型字段,导出时没显式指定
NLS_LANG就会丢数据
NLS_LANG 设置错误导致 expdp 导出乱码
导出时如果客户端 NLS_LANG 和源库字符集不一致,expdp 会按错误编码解释数据,再以目标字符集重新编码——结果就是双倍乱码,且不可逆。
例如源库是 ZHS16GBK,但终端环境变量设成 AMERICAN_AMERICA.AL32UTF8,导出的 DMP 文件里中文就变成 UTF-8 字节序列,再导入到 AL32UTF8 库里,实际存的是“UTF-8 编码的 UTF-8 编码”。
- 导出前必须确认:
SELECT * FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER = 'NLS_CHARACTERSET'; - 然后在 shell 中执行:
export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK(与源库一致) - 导入时同理:
NLS_LANG必须匹配目标库字符集,不是源库 - Windows 下用
set NLS_LANG=...,注意不要有空格
expdp/impdp 过程中 CLOB/NVARCHAR2 容易丢失或截断
AL32UTF8 下一个汉字占 3 字节,ZHS16GBK 下占 2 字节。如果表结构里定义了 CHAR(10) 或 VARCHAR2(100),切换字符集后,同样长度能存的汉字数会变——但 Oracle 不自动调整列宽,也不报错,只静默截断。
更麻烦的是 CLOB:它本身不直接受字符集影响,但若源库用 ZHS16GBK 存了 4000 字节的 CLOB,导入到 AL32UTF8 后,这些字节被当 UTF-8 解码,可能因非法序列被替换成 ,且无法恢复。
- 导出前运行:
SELECT owner, table_name, column_name, data_type FROM dba_tab_columns WHERE data_type IN ('CHAR','VARCHAR2','NCHAR','NVARCHAR2') AND data_length > 0;记录所有定长字段 - 对含中文的
CLOB字段,建议先导出为文本文件(用UTL_FILE或 SQL*PlusSPOOL),人工校验编码 - 导入后立刻检查:
SELECT DUMP(column_name, 1016) FROM table_name WHERE ROWNUM = 1;看十六进制值是否符合预期
12c+ 数据库必须用 DBMS_DATAPUMP 替代 exp/imp
传统 exp/imp 工具在 12c 起已废弃,不支持多字节字符集的元数据正确处理,尤其涉及 XMLType、JSON 或分区表时,导入后对象状态常为 INVALID。
expdp 虽是推荐方式,但它默认跳过某些系统对象(如 DBA_REGISTRY 中的组件信息),若库中有 Oracle Text、Spatial 等组件,需显式加 INCLUDE=SCHEMA:"IN ('CTXSYS','MDSYS')",否则功能异常。
- 必须用
VERSION=12参数导出,否则低版本 impdp 无法识别高版本元数据 - 避免用
REMAP_SCHEMA同时改 schema 名和字符集,容易触发ORA-39126内部错误 - 导入时加上
TRANSFORM=SEGMENT_ATTRIBUTES:n可跳过存储参数冲突,但需后续手工重建索引
字符集转换不是配置开关,是数据重编码过程;哪怕脚本跑通,也得逐表比对 DUMP() 结果和业务查询表现——最危险的不是报错,是安静地存错了。










