Oracle中文乱码或截断主因是NLS_LANG未对齐服务端字符集或字段类型不支持多字节字符;须先查NLS_DATABASE_PARAMETERS确认真实字符集,再匹配NLS_LANG并慎用NVARCHAR2。

Oracle中文乱码或截断,90% 以上不是数据库坏了,而是 NLS_LANG 没对齐服务端字符集,或者字段类型不支持多字节字符。改错地方反而会让数据更糟。
查清服务端真实字符集再动手
别猜、别试、别抄网上的 AL32UTF8 就设上。先连进数据库执行:
SELECT VALUE FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER = 'NLS_CHARACTERSET';
返回值才是你必须对齐的字符集。常见结果有:ZHS16GBK(Windows 中文环境常见)、AL32UTF8(新装库)、WE8ISO8859P1(西欧默认,存中文必乱)。如果返回 US7ASCII,那它根本不能原生存中文——后续所有客户端设置都只是“掩耳盗铃”。
- 注意:
SELECT USERENV('language') FROM DUAL返回的是会话级 NLS 设置,可能被ALTER SESSION覆盖,不能代替上面那条 - 如果数据库是
ZHS16GBK,客户端NLS_LANG就必须是AMERICAN_AMERICA.ZHS16GBK,少一个点、大小写错、多空格都会失效 -
AL32UTF8和UTF8是两个不同字符集,Oracle 中UTF8是旧版,不完全兼容 Unicode,别混用
SQL*Plus 和 PL/SQL Developer 的 NLS_LANG 不互通
你在终端里 export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK,然后启动 SQL*Plus —— 这个设置生效;但双击打开的 PL/SQL Developer 或 Toad 完全不读这个环境变量。
- PL/SQL Developer:进
Tools → Preferences → Oracle → Connection,手动填完整值,例如AMERICAN_AMERICA.ZHS16GBK - Toad:连接配置页底部有
NLS Settings区域,同样填完整三段式字符串 - SQL*Plus 启动前必须确保 shell 环境已设好,且检查
$ORACLE_HOME/sqlplus/admin/glogin.sql里没有SET NLS_类语句覆盖你的设置 - Windows 下用命令行临时设:
set NLS_LANG=AMERICAN_AMERICA.ZHS16GBK;注册表路径为HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\KEY_<your_oracle_home></your_oracle_home>,键名NLS_LANG
中文被截断?很可能是 varchar2 字段按字节计长
比如建表时写 name VARCHAR2(10),在 ZHS16GBK 库里,一个汉字占 2 字节,最多存 5 个汉字;在 AL32UTF8 库里,常用汉字占 3 字节,最多存 3 个汉字。插入第 4 个字就静默截断,不会报错。
- 确认方式:执行
SELECT DUMP('张三李四', 1016) FROM DUAL,看输出字节数。例如TYP=96 LEN=6: 5f,4e,4e,65,6c,3a表示 6 字节,对应 3 个 GBK 汉字 - 治本方法:把字段改成
NVARCHAR2(10),它按字符计长,且使用国家字符集(NLS_NCHAR_CHARACTERSET),通常为AL32UTF8,能无损存生僻字 - 插入时必须加
n'张三'前缀,否则仍走VARCHAR2路径,可能二次转码 - 已有数据迁移需用
ALTER TABLE t MODIFY c NVARCHAR2(10),但注意:含数据的列改类型可能失败,需先导出再重建
生僻字(如䶮、?、?)显示为 ? 或方块
ZHS16GBK 只覆盖约 21000 个汉字,GB18030 或 AL32UTF8 才支持超 8 万字。但直接改库字符集风险极高,多数场景应绕过而非硬改。
- 优先方案:把字段类型从
VARCHAR2改成NVARCHAR2,并确保客户端NLS_LANG中的字符集支持 Unicode(如AL32UTF8) - 插入时强制用 Unicode 字面量:
INSERT INTO t VALUES (n'䶮');,而不是'䶮' - 验证存储是否正确:
SELECT DUMP(n'䶮', 1016) FROM DUAL,应返回类似TYP=1 LEN=4: e9,9a,91(UTF-8 编码) - 如果业务系统用 JDBC,不用碰
NLS_LANG,改连接串加参数:?useUnicode=true&characterEncoding=UTF-8
最易被忽略的一点:NLS_LANG 只管「传输时怎么解码字节」,不管「字节本身对不对」。如果你用 ZHS16GBK 客户端往 AL32UTF8 库插数据,而原始输入其实是 UTF-8 编码的字节,Oracle 会把它当 GBK 解,再转 UTF-8 存——结果就是双乱码,且不可逆。所以第一步永远是确认源头编码,而不是调参数。


















