- 论坛徽章:
- 0
|
好友的客户碰到了这样一个问题----通过前台导入数据后发现前台显示这些导入的数据正常,但是用sqlplus看这些新导入的数据全部是乱码。
后来被我发现导致这种现象的原因是前台导入的时候错误的选择了Unicode字符集对源数据的GBK编码做了转换,且NLS_LANG和数据库的NLS_CHARACTERSET一致(都是ZHS16GBK)。而且更要命的是朋友客户刚刚改过NLS_LANG(以前的NLS_LANG设的是AL32UTF8),这直接导致了数据库中有部分数据是GBK编码,而另外一部分数据则是Unicode编码。
Oracle里的字符集的正确理解涉及到NLS_LANG,NLS_CHARACTERSET和客户端OS的字符集这三方面,任何一个不对,都会导致乱码的出现。
我们来看一个实际的例子,我现在库的NLS_CHARACTERSET是ZHS16GBK,我首先想办法以Unicode编码(AL32UTF8)插入字符'美好 ',同时再以GBK编码(ZHS16GBK)也同时插入字符'美好 ':
SQL_testdb>select name,dump(name,16) from t_c;
NAME DUMP(NAME,16)
---------------------------------------- -----------------------------------------------------
缇庡ソ Typ=1 Len=7: e7,be,8e,e5,a5,bd,20
美好 Typ=1 Len=5: c3,c0,ba,c3,20
这里就构造出了朋友客户那边的实际情况,NLS_CHARACTERSET为ZHS16GBK的库,但是库里的数据却同时有Unicode和GBK两种编码。
朋友问我----有没有办法让库里已经存在的Unicode编码的数据在sqlplus中正常的显示?
这里是没有办法的,即使把NLS_LANG设置成了AL32UTF8,或者说除非你能找到一个OS是Unicode编码的客户端。这是因为NLS_CHARACTERSET始终都是ZHS16GBK,当你把NLS_LANG设置成了AL32UTF8后,oracle会把已经是Unicode编码的字符'美好 '再次以oracle内置的字符集转换规则从GBK编码转换为Unicode编码:
[P550_04_LA racle@:/dras20/testdb]#export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
SQL_testdb>select name,dump(name,16) from t_c;
NAME DUMP(NAME,16)
-------------------- --------------------------------------------------------
缂囧骸銈? Typ=1 Len=7: e7,be,8e,e5,a5,bd,20
缇庡ソ Typ=1 Len=5: c3,c0,ba,c3,20
从结果里我们可以看到,现在显示结果更乱了:)
但其实还是有办法让Unicode编码的字符'美好 '正常显示的,只不过这种方法没有实际意义。
我们现在来让Unicode编码的字符'美好 '在sqlplus中恢复正常显示:
[P550_04_LA racle@:/dras20/testdb]#export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK
[P550_04_LA racle@:/dras20/testdb]#sqlplus '/ as sysdba';
SQL*Plus: Release 9.2.0.6.0 - Production on Wed Nov 17 08:44:42 2010
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
Connected to:
Oracle9i Enterprise Edition Release 9.2.0.6.0 - 64bit Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.6.0 - Production
SQL_testdb>update props$ set value$='AL32UTF8' where name='NLS_CHARACTERSET';
1 row updated.
SQL_testdb>commit;
Commit complete.
SQL_testdb>select name,value$ from props$ where name='NLS_CHARACTERSET';
NAME VALUE$
-------------------- -----------------------------------------------------------------------------
NLS_CHARACTERSET AL32UTF8
SQL_testdb>shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL_testdb>startup pfile=/dras20/testdb/inittestdb.ora
ORACLE instance started.
Total System Global Area 504858456 bytes
Fixed Size 743256 bytes
Variable Size 285212672 bytes
Database Buffers 218103808 bytes
Redo Buffers 798720 bytes
Database mounted.
Database opened.
SQL> select name,dump(name,16) from t_c;
NAME DUMP(NAME,16)
---------------------------------------- -----------------------------------------------------------
美好 Typ=1 Len=7: e7,be,8e,e5,a5,bd,20
??? Typ=1 Len=5: c3,c0,ba,c3,20
这里的原理就是利用了Oracle只会根据NLS_LANG和数据库的NLS_CHARACTERSET来做字符集的转换,当我把NLS_LANG设为ZHS16GBK,NLS_CHARACTERSET修改为AL32UTF8后,Oracle此时就会把字符'美好 '的Unicode编码07,e7,be,8e,e5,a5,bd,20转换为GBK编码05,c3,c0,ba,c3,20,同时又因为我的客户端OS的字符集编码就是GBK,所以这里我就能正确的看到字符'美好 '了。
有朋友看到这里可能会问:你只修改了props$,但是控制文件里记录的数据库字符集还是ZHS16GBK,你这里已经不一致了。
没关系的,oracle不会允许这种不一致存在,如下是alert log里的相关内容:
Wed Nov 17 10:17:53 2010
SMON: enabling tx recovery
Wed Nov 17 10:17:53 2010
Updating character set in controlfile to AL32UTF8
这跟数据库在open的时候oracle会根据file$的内容去修改控制文件(如果需要的话)多么的相似! |
|