先看清三个关键角色
执行 SQL 文件时,至少涉及三处字符集:
Linux Locale:由 LANG 或 LC_CTYPE 控制,影响 Shell 如何处理文本。
SQL*Plus 客户端字符集:由 NLS_LANG 控制。
SQL 文件实际编码:常见是 UTF-8 或 GBK。
核心原则很简单:
SQL 文件编码要和 NLS_LANG 对应的客户端字符集一致,再由 Oracle 负责客户端与数据库之间的字符集转换。
这里最容易误解的是:NLS_LANG 表示客户端字符集,不是数据库字符集。
02 | UTF-8 文件的推荐配置
大多数 Linux 环境和编辑器默认已经使用 UTF-8,因此更推荐统一使用 UTF-8。
export LANG=en_US.UTF-8
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
如果使用中文 Locale,也可以是:
export LANG=zh_CN.UTF-8
export NLS_LANG=SIMPLIFIED CHINESE_CHINA.AL32UTF8
SQL 文件建议保存为:
UTF-8
无 BOM,也就是 UTF-8 without BOM
如果 SQL 文件本身是 GBK 编码,才使用:
export LANG=zh_CN.GBK
export NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK
03 | 不要盲目照抄数据库字符集
很多人看到数据库字符集是 ZHS16GBK,就直接设置:
export NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK
这只有在客户端 SQL 文件本身也是 GBK 编码时才合适。
如果 SQL 文件是 UTF-8,而数据库字符集是 ZHS16GBK,客户端仍然应该按 UTF-8 设置:
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
此时 Oracle 会完成字符集转换:
UTF-8(客户端)
↓
Oracle Character Conversion
↓
ZHS16GBK(数据库)
所以排查时不要只盯数据库字符集,更要先确认 SQL 文件到底是什么编码。
04 | 如何确认文件和数据库字符集
查看 SQL 文件编码,优先用:
file test.sql
file -i test.sql
如果输出中看到 charset=utf-8,说明是 UTF-8。若显示 UTF-8 Unicode (with BOM),说明文件带 BOM。
也可以用 iconv 验证 UTF-8 是否合法:
iconv -f UTF-8 -t UTF-8 test.sql >/dev/null
echo $?
返回 0 表示合法 UTF-8;如果出现 illegal input sequence,说明不是合法 UTF-8。
检查是否带 BOM:
xxd -l 16 test.sql
如果文件开头出现:
ef bb bf
说明带 BOM。
数据库字符集可以用 SQL 查询:
SELECT value
FROM nls_database_parameters
WHERE parameter = ‘NLS_CHARACTERSET’;
常见结果可能是 AL32UTF8 或 ZHS16GBK。
05 | LC_ALL 不一定要设置
通常不需要刻意设置 LC_ALL。Locale 优先级大致是:
LC_ALL
↓
LC_*
↓
LANG
SQL*Plus 中文乱码更相关的是:
LANG
LC_CTYPE
NLS_LANG
很多环境只设置下面这一项就够了:
export LANG=en_US.UTF-8
LC_ALL 是强制覆盖所有 Locale 设置,并不是解决 SQL*Plus 中文乱码的关键。有些系统还可能没有安装对应 Locale,导致 cannot change locale。可以先确认系统支持项:
locale -a
06 | 一套稳妥排查顺序
遇到乱码时,建议依次执行:
locale
echo $LANG
echo $NLS_LANG
file -i test.sql
再查数据库字符集:
SELECT value
FROM nls_database_parameters
WHERE parameter=‘NLS_CHARACTERSET’;