在 Linux 上用 sqlplus @xxx.sql 执行脚本时,只要 SQL 文件里有中文字符串或中文注释,就可能遇到乱码。这个问题通常不是数据库本身坏了,而是 SQL 文件编码、Linux Locale、SQL*Plus 客户端字符集没有对齐。
01 | 先看清三个关键角色
执行 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。
bash
export LANG=en_US.UTF-8
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
如果使用中文 Locale,也可以是:
bash
export LANG=zh_CN.UTF-8
export NLS_LANG=SIMPLIFIED CHINESE_CHINA.AL32UTF8
SQL 文件建议保存为:
- UTF-8
- 无 BOM,也就是 UTF-8 without BOM
如果 SQL 文件本身是 GBK 编码,才使用:
bash
export LANG=zh_CN.GBK
export NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK
03 | 不要盲目照抄数据库字符集
很多人看到数据库字符集是 ZHS16GBK,就直接设置:
bash
export NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK
这只有在客户端 SQL 文件本身也是 GBK 编码时才合适。
如果 SQL 文件是 UTF-8,而数据库字符集是 ZHS16GBK,客户端仍然应该按 UTF-8 设置:
bash
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
此时 Oracle 会完成字符集转换:
text
UTF-8(客户端)
↓
Oracle Character Conversion
↓
ZHS16GBK(数据库)
所以排查时不要只盯数据库字符集,更要先确认 SQL 文件到底是什么编码。
04 | 如何确认文件和数据库字符集
查看 SQL 文件编码,优先用:
bash
file test.sql
file -i test.sql
如果输出中看到 charset=utf-8,说明是 UTF-8。若显示 UTF-8 Unicode (with BOM),说明文件带 BOM。
也可以用 iconv 验证 UTF-8 是否合法:
bash
iconv -f UTF-8 -t UTF-8 test.sql >/dev/null
echo $?
返回 0 表示合法 UTF-8;如果出现 illegal input sequence,说明不是合法 UTF-8。
检查是否带 BOM:
bash
xxd -l 16 test.sql
如果文件开头出现:
text
ef bb bf
说明带 BOM。
数据库字符集可以用 SQL 查询:
sql
SELECT value
FROM nls_database_parameters
WHERE parameter = 'NLS_CHARACTERSET';
常见结果可能是 AL32UTF8 或 ZHS16GBK。
05 | LC_ALL 不一定要设置
通常不需要刻意设置 LC_ALL。Locale 优先级大致是:
text
LC_ALL
↓
LC_*
↓
LANG
SQL*Plus 中文乱码更相关的是:
LANGLC_CTYPENLS_LANG
很多环境只设置下面这一项就够了:
bash
export LANG=en_US.UTF-8
LC_ALL 是强制覆盖所有 Locale 设置,并不是解决 SQL*Plus 中文乱码的关键。有些系统还可能没有安装对应 Locale,导致 cannot change locale。可以先确认系统支持项:
bash
locale -a
06 | 一套稳妥排查顺序
遇到乱码时,建议依次执行:
bash
locale
echo $LANG
echo $NLS_LANG
file -i test.sql
再查数据库字符集:
sql
SELECT value
FROM nls_database_parameters
WHERE parameter='NLS_CHARACTERSET';
笔者更推荐的长期做法是:团队统一使用 UTF-8 无 BOM 的 .sql 文件,并配置:
bash
export LANG=en_US.UTF-8
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
这样可以减少 UTF-8、GBK 混用带来的跨平台问题。
一句话总结:保持 SQL 文件编码、Linux Locale 和 NLS_LANG 一致,让 Oracle 负责客户端与数据库之间的字符集转换,是避免 SQL*Plus 中文乱码的稳妥方案。
关注我,和AI一起成长~