暂无图片
暂无图片
7
暂无图片
暂无图片
暂无图片

Oracle NLS字符集

原创 Thomas 2021-11-05
3262

定义
这里的字符集是广义的概念,涵盖了三个设置,分别是语言、国家和字符集,一般表示方式为:
语言_国家.字符集。
NLS: national language support

常用字符集介绍
ZHS16GBK是中文字符集,也就是适合在中国用,只能存储中文和英文字符,如果你存储韩文则显示为乱码(没有编码),而AL32UTF8是utf8字符集,u是unicode的意思,适合中文、韩语、日语等等不同的语言使用。那么为什么我们要在中国使用zhs16gbk存储中文呢?这是因为utf8存储中文的效率不如ZHS16GBK,比如一个字“懂”,ZHS16GBK采用2个字符(2 bytes,16bits,所以叫ZHS16)存储,而AL32UTF8采用3-4个字符(4 bytes,32bits,所以叫AL32)存储,这样效率就有了高低之分。
Unicode:包含了几乎人类所有可用的字符,每年还在不断的增加,可以看作是一种通用的字符集。它将全世界所有的字符统一化,统一编码,不会再出现字符不兼容和字符转换的问题。它有以下三种编码方式:
UTF-32编码:固定使用4个字节来表示一个字符,存在空间利用效率的问题。
UTF-16编码:对相对常用的60000余个字符使用两个字节进行编码,其余的使用4字节。
UTF- 8编码:兼容ASCII编码;拉丁文、希腊文等使用两个字节;包括汉字在内的其它常用字符使用三个字节;剩下的极少使用的字符使用四个字节。

DB端
DB端字符集(这里仅仅指nls_characterset这一项)是在安装时设置的,设置好后能否修改呢?官方文档说:
only if the new character set is a strict superset of the current character set.
就是说,只有新字符集是旧字符集的超集时,才可以改。否则,有可能造成data corruption.

如果安装Oracle时没有指定NLS_LANG,则默认值是AMERICAN_AMERICA.US7ASCII。

运行此语句可以一次性查出DB端的字符集设置:
SQL> select (select value from nls_database_parameters where parameter=‘NLS_LANGUAGE’)||’_’||(select value from nls_database_parameters where parameter=‘NLS_TERRITORY’)||’.’||(select value from nls_database_parameters where parameter=‘NLS_CHARACTERSET’) as DB_NLS_LANG from dual;

DB_NLS_LANG
--------------------------------------------------------------------------------------------------------------------------------------AMERICAN_AMERICA.ZHS16GBK

客户端
说完了DB端字符集,再聊聊客户端的NLS_LANG。

NLS_LANG确定了ORACLE客户端程序(比如SQLPLUS,也比如一切利用ORACLE客户端或者INSTANT CLIENT连入DB的应用程序)运行时的字符集,那么NLS_LANG在哪里设置呢?
Windows下,在注册表里设置(一般在安装ORACLE客户端时自动生成),也可以在系统环境变量里设,环境变量里的设置优先于注册表设置。
Linux下,在oracle用户的.bash_profile下设置。
Unix下,在oracle用户的.profile下设置。

只有设置正确了NLS_LANG里的NLS_CHARCTERSET项,才可以保证ORACLE客户端程序运行时不乱码。

一句话:NLS_LANG里的NLS_CHARCTERSET的设置,可以和DB端的NLS_CHARCTERSET不一致(因为与DB端交互时,系统会自动做字符集转换),但必须和OS的NLS_CHARCTERSET项一致,否则便会乱码。但一般来说,把NLS_LANG里的三项和OS里的三项设置得都一致,应该较为稳妥。

步骤可以归纳为:
1.找到操作系统使用的NLS_CHARACTERSET,并找到其对应的ORACLE NLS_CHARACTERSET。
2.修改Oracle用户环境变量NLS_LANG里的NLS_CHARACTERSET部分,确保其与OS下的设置一致。

咱先看看如何查看NLS_LANG的值吧:
Linux/Unix下,echo $NLS_LANG
Windows下,cmd窗口运行set NLS_LANG查看环境变量值,或者到注册表里查询NLS_LANG这一项的内容。

查到后,再显示下OS上的设置:
Linux/Unix:echo $LANG或者敲locale命令。
Windows: cmd窗口敲chcp命令,得到字符集代码
例如:
C:\Users\Administrator>chcp
活动代码页: 936
代码936代表 中国 - 简体中文(GB2312)

那么,如何改OS上的设置呢:
Linux/Unix:vi /etc/locale.conf 或 /etc/sysconfig/i18n
Windows: cmd窗口敲chcp命令后跟新的活动页代码,即生效。

如果LANG为en_US.UTF-8, 则NLS_LANG的CHARACTERSET部分也为UTF-8?非也!应该是AL32UTF8。在oracle的NLS_LANG字符集和OS的字符集之间,存在一张对照表,该表具体信息,目前网上尚未搜到。

查询V$NLS_VALID_VALUES, 可以得到可用的NLS_CHARACTERSET值:

SELECT value FROM V$NLS_VALID_VALUES WHERE PARAMETER=‘CHARACTERSET’ and isdeprecated=‘FALSE’ order by value;

如果OS层面的LANG为zh_CN.GB18030,则Oracle用户层面NLS_LANG,三项全统一的话,为Simplified Chinese_China.ZHS32GB18030,如下:
echo $LANG
zh_CN.GB18030
echo $NLS_LANG
Simplified Chinese_China.ZHS32GB18030
环境变量设置:export NLS_LANG=“Simplified Chinese_CHINA.ZHS32GB18030”

前文已经说了查询DB端字符集情况,下面谈谈查询当前SESSION字符集情况。这里说当前session,突出了一个动态的概念,即当下session的情况,既不是oracle用户的环境变量NLS_LANG,也不是OS的LANG。实际上,这个值属于组合拳:当前SESSION的NLS_LANGUAGE + 当前SESSION的 NLS_TERRITORY + DB端的CHARACTERSET。当然,如果SESSION连入后未运行alter session做特殊设置,那么当前SESSION的NLS_LANGUAGE和NLS_TERROIROTY照搬NLS_LANG的设置。

可以从v$nls_parameters查:

SQL> select (select value from vnls_parameters where parameter='NLS_LANGUAGE')||'_'||(select value from vnls_parameters where parameter=‘NLS_TERRITORY’)||’.’||(select value from v$nls_parameters where parameter=‘NLS_CHARACTERSET’) as SESSION_NLS_LANG from dual;

SESSION_NLS_LANG
--------------------------------------------------------------------------------------------------------------------------------------SIMPLIFIED CHINESE_CHINA.ZHS16GBK

也可以运行select userenv(‘language’) from dual; 得到同样结果。

示例

演示一个登录后改设置的例子:
SQL> select userenv(‘language’) from dual;

USERENV(‘LANGUAGE’)
----------------------
FRENCH_AMERICA.ZHS16GBK

SQL> select cname from tb_city_basic where code=‘NKG’;

CNAME
------------------------------------------------------------
南京

SQL> alter session set nls_territory=‘Mexico’;

Session altered.

SQL> select userenv(‘language’) from dual;

USERENV(‘LANGUAGE’)
----------------------
FRENCH_MEXICO.ZHS16GBK

尽管session里把NLS_LANGUAGE和NLS_TERRITORY设置为French和Mexico, 但因为NLS_LANG里的NLS_CHARACTERSET为AL32UTF8,而OS里为UTF8,两者是一致的,所以并不会出现乱码。
注意这里显示的ZHS16GBK为DB端设置,与NLS_LANG无关。另外,在SESSION也无alter session set nls_characterset=xxx; 的说法。换言之,一旦出现乱码,应该查看OS层面的环境变量,而用alter session命令毫无用处。
SQL> select cname from tb_city_basic where code=‘NKG’;

CNAME
------------------------------------------------------------
南京

下面的试验,可以看出,乱码与否,起决定因素的还是NLS_LANG和LANG里的NLS_CHARACTERSET:
[oracle@iZ94t0nyo72Z ~]$ echo LANG en\_US.UTF-8 \[oracle@iZ94t0nyo72Z ~\] echo $NLS_LANG
ENGLISH_AMERICA.WE8ISO8859P1
连入DB,运行select出现乱码了,原因就是NLS_LANG里的WE8ISO8859P1与OS里的设置UFT-8不一致:
SQL> select userenv(‘language’) from dual;

USERENV(‘LANGUAGE’)
----------------------------------------------------
ENGLISH_AMERICA.ZHS16GBK

SQL> select cname from tb_city_basic where code=‘NKG’;

CNAME
--------------------
??

最后修改时间:2024-10-12 07:41:27
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论