[20241118]NLS_LANG设置问题2.txt
--//链接 https://www.itpub.net/thread-2155589-1-1.html上的讨论。
--//PiscesCanon指出:
--//NLS_LANG设置错了,如果你的客户端是sqlplus,那么应该是NLS_LANG=.AL32UTF8或者NLS_LANG=AMERICAN_AMERICA.AL32UTF8,跟着
--//OS的字符集来。另外,SecureCRT或者xshell这类工具本身有自己的字符集,那么也要设置为UTF8。
--//NLS_LANG这个环境变量的作用就是告诉数据库,你客户端(sqlplus、plsql dev之类的)所使用的字符集是什么。这样我数据库才能根
--//据客户端过来的对应字符集编码根据编码表转化为数据库本身的编码来存储。
--//跟我以前的理解或者一般人的理解不一样,测试看看。
--//我以前的测试链接:[20240820]字符集与dml语句.txt
1.环境:
SCOTT@book01p> @ver2
==============================
PORT_STRING : x86_64/Linux 2.4.xx
VERSION : 21.0.0.0.0
BANNER : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
BANNER_FULL : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
Version 21.3.0.0.0
BANNER_LEGACY : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
CON_ID : 0
PL/SQL procedure successfully completed.
--//linux:
# cat /etc/redhat-release
CentOS Linux release 7.3.1611 (Core)
# echo $LANG
en_US.UTF-8
SCOTT@book01p> select * from v$nls_parameters ;
PARAMETER VALUE CON_ID
------------------------------ ------------------------------------ ----------
NLS_LANGUAGE AMERICAN 3
NLS_TERRITORY AMERICA 3
NLS_CURRENCY $ 3
NLS_ISO_CURRENCY AMERICA 3
NLS_NUMERIC_CHARACTERS ., 3
NLS_CALENDAR GREGORIAN 3
NLS_DATE_FORMAT YYYY-MM-DD HH24:MI:SS 3
NLS_DATE_LANGUAGE AMERICAN 3
NLS_CHARACTERSET ZHS16GBK 3
NLS_SORT BINARY 3
NLS_TIME_FORMAT HH.MI.SSXFF AM 3
NLS_TIMESTAMP_FORMAT YYYY-MM-DD HH24:MI:SS.FF 3
NLS_TIME_TZ_FORMAT HH24.MI.SSXFF TZH:TZM 3
NLS_TIMESTAMP_TZ_FORMAT YYYY-MM-DD HH24:MI:SS.FF TZH:TZM 3
NLS_DUAL_CURRENCY $ 3
NLS_NCHAR_CHARACTERSET AL16UTF16 3
NLS_COMP BINARY 3
NLS_LENGTH_SEMANTICS BYTE 3
NLS_NCHAR_CONV_EXCP FALSE 3
19 rows selected.
--//建立数据库字符集NLS_CHARACTERSET=ZHS16GBK,而NLS_NCHAR_CHARACTERSET=AL16UTF16,这种情况几乎是前几年大部分数据库的建立
--//模式,至少我们团队建立的数据库基本都是这样.
$ env | grep -i nls
NLS_LANG=AMERICAN_AMERICA.ZHS16GBK
NLS_TIMESTAMP_TZ_FORMAT=YYYY-MM-DD HH24:MI:SS.FF TZH:TZM
NLS_TIMESTAMP_FORMAT=YYYY-MM-DD HH24:MI:SS.FF
NLS_TIME_TZ_FORMAT=HH24.MI.SSXFF TZH:TZM
NLS_DATE_FORMAT=YYYY-MM-DD HH24:MI:SS
--//以前的测试定义NLS_LANG=AMERICAN_AMERICA.ZHS16GBK,这次测试设置NLS_LANG=AMERICAN_AMERICA.AL32UTF8看看。
2.测试:
$ export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
SCOTT@book01p> create table t1 (id number ,vc varchar2(20),nvc nvarchar2(40));
Table created.
--//在linux服务端插入:
SCOTT@book01p> insert into t1 values (1,'1测试2','a测试b');
1 row created.
SCOTT@book01p> commit;
Commit complete.
--//在linux服务端查询:
SCOTT@book01p> column vc format a20
SCOTT@book01p> column nvc format a20
SCOTT@book01p> select * from t1;
ID VC NVC
---------- -------------------- --------------------
1 1测试2 a测试b
--//在windows客户端查询:
D:\tmp\study> set | grep -i nls_l
NLS_LANG=AMERICAN_AMERICA.ZHS16GBK
SCOTT@book01p> insert into t1 values (2,'3测试4','c测试d');
1 row created.
SCOTT@book01p> commit;
Commit complete.
SCOTT@book01p> select * from t1;
ID VC NVC
---------- -------------------- -----------------------------------
1 1测试2 a测试b
2 3测试4 c测试d
--//确实如此,颠覆我以前的认知。
--//在linux服务端查询:
SCOTT@book01p> select dump(vc,16) c40 ,dump(nvc,16) c50 from t1 ;
C40 C50
---------------------------------------- --------------------------------------------------
Typ=1 Len=6: 31,b2,e2,ca,d4,32 Typ=1 Len=8: 0,61,6d,4b,8b,d5,0,62
Typ=1 Len=6: 33,b2,e2,ca,d4,34 Typ=1 Len=8: 0,63,6d,4b,8b,d5,0,64
--//b2,e2,ca,d4 = 测试
--//b2,e2,ca,d4 的GBK编码确实是 "测试"。
3.总结:
--//看来我们以前许多人的理解严重错误,重复前面的提示:NLS_LANG这个环境变量的作用就是告诉数据库,你客户端(sqlplus、plsql
--//dev之类的)所使用的字符集是什么。这样我数据库才能根据客户端过来的对应字符集编码根据编码表转化为数据库本身的编码来存储。
--//NLS_LANG的设置要与OS字符集保持一致,而不是跟着数据库里面的设置!!
--//不过第一次设置看上去感觉非常别扭,违法以前的认知。我个人建议如果有中文还是尽量减少在服务端操作,在windows客户端操作
--//完成。我也已经发现生产系统建立包过程一些注解是乱码,我不知道同事是在windows下还是liunx下完成操作完成,总之同事建立与
--//维护有点太不上心,建立完成就ok了,事后没有使用工具仔细检查,注解全是乱码,如果语句涉及中文就很麻烦了。
--//我总有一种感觉,统一到utf-8也许是大势所趋,至少许多软件都有这个的趋势.我下载最新vim 9.0,kitty,安装最新的linux发布都遇
--//到类似的问题.
4.补充我还发现一个细节问题,显示宽度问题:
--//在windows下执行,NLS_LANG=AMERICAN_AMERICA.ZHS16GBK:
SCOTT@book01p> select dump('彩',16),dump('测试',16) from dual ;
DUMP('彩',16) DUMP('测试',16)
------------------- -------------------------
Typ=96 Len=2: b2,ca Typ=96 Len=4: b2,e2,ca,d4
--//在linux服务端下执行,NLS_LANG=AMERICAN_AMERICA.AL32UTF8:
SCOTT@book01p> select dump('彩',16),dump('测试',16) from dual ;
DUMP('彩',16) DUMP('测试',16)
--------------------------------------------------------- ---------------------------------------------------------------------------
Typ=96 Len=2: b2,ca Typ=96 Len=4: b2,e2,ca,d4
--//linux下宽度增加3倍数,占用19,显示宽度57.
SCOTT@book01p> select dump('1',16) from dual ;
DUMP('1',16)
------------------------------------------------
Typ=96 Len=1: 31
--//没有汉字也是一样。
--//在linux服务端下执行,NLS_LANG=AMERICAN_AMERICA.ZHS16GBK
SCOTT@book01p> select dump('彩',16),dump('测试',16) from dual ;
ERROR:
ORA-01756: quoted string not properly terminated
--//出现问题,如果检查单引号没有问题的情况下特别注意字符集相关问题,为什么出现这样的问题呢?
SYS@book> select dump('1彩2',16),dump('测试',16) from dual ;
DUMP('1彩2',16) DUMP('测试',16)
---------------------------- -------------------------------
Typ=96 Len=5: 31,e5,bd,a9,32 Typ=96 Len=6: e6,b5,8b,e8,af,95
--//实际上输入的是UTF8的编码,不是ZHS16GBK编码,彩在转换是e5bda9,这样在ZHS16GBK出现半个汉字,导致错误。
--//在结尾加入一个空格正常。
SCOTT@book01p> select dump('彩 ',16) ,dump('测试',16) from dual ;
DUMP('彩 ',16) DUMP('测试',16)
------------------------- -------------------------------
Typ=96 Len=4: e5,bd,a9,20 Typ=96 Len=6: e6,b5,8b,e8,af,95
--//如果在这样的情况下linux下插入在windows下的显示就是出现乱码。
SCOTT@book01p> insert into t1 values (3,'彩 ','e测试f');
1 row created.
SCOTT@book01p> commit ;
Commit complete.
SCOTT@book01p> select * from t1;
ID VC NVC
---------- -------------------- ---------
1 1? a?
2 3? c?
3 彩 e测试f
--//你以为显示正常,实际上显示的是UTF8的编码。
--//在windows下执行显示如下:
SCOTT@book01p> select * from t1;
ID VC NVC
---------- -------------------- ----------------------
1 1测试2 a测试b
2 3测试4 c测试d
3 褰? e娴嬭瘯f
SCOTT@book01p> select id,dump(vc,16) c50,dump(nvc,16) c50 from t1;
ID C50 C50
---------- -------------------------------------------------- --------------------------------------------------
1 Typ=1 Len=6: 31,b2,e2,ca,d4,32 Typ=1 Len=8: 0,61,6d,4b,8b,d5,0,62
2 Typ=1 Len=6: 33,b2,e2,ca,d4,34 Typ=1 Len=8: 0,63,6d,4b,8b,d5,0,64
3 Typ=1 Len=4: e5,bd,a9,20 Typ=1 Len=10: 0,65,5a,34,5b,2d,76,2f,0,66