提及Oracle字符集可以说是老生常,Oracle字符集问题的引起,主要是因为其的“乱码”问题。而乱码的产生主要是因为客户端和服务器的实际应用的字符集不同,其进行字符集转换而引起的。
不过很多提到了转换,却没有提到这个转换是在哪个阶段和哪里发生的?是在服务器向块里写入数据的时候吗?在客户端还是在服务器端?
正确的答案是,普通字符串转换发生在客户端(具体来说是由OCI LIBRARY完成的),国家字符串经过两次转换,***次发生在客户端,第二次发生在服务器端。下面做个测试:
连接到:
OracleDatabase10gEnterpriseEditionRelease10.2.0.1.0-Production WiththePartitioning,RealApplicationClusters,OLAPandDataMiningoptions SQL>select*fromnls_database_parameterswhereparameterlike‘%CHARACTERSET%’; PARAMETERVALUE ------------------------------------------------------------ NLS_CHARACTERSETZHS16GBK NLS_NCHAR_CHARACTERSETAL16UTF16 SQL>createtablet1(avarchar2(100));
表已创建。
SQL> SQL>insertintot1values(’中’);
已创建 1 行。
SQL>
在本次连接中,我没有设置NLS_LANG变量。则客户端Oracle字符集为操作系统的缺省字符集ZHS16GBK。通过捕获网络包,可以发现客户端传送给客户端的数据(不能上传图片,郁闷):
00000090000000000000000000000028DB00011C………..(…. 000000A0696E7365727420696E746F2074312076insert.into.t1.v 000000B0616C756573202827D6D0272901000000alues.(’..’)…. 000000C001000000000000000000000000000000…………….
注意红色的部分,16进制D6 D0正是“中”字的GBK编码。(关于怎么获取汉字的各种编码,暂且略过,如有需要再交流)
现在我们退出SQLPLUS,设置环境变量NLS_LANG:
SQL>rollback;
回退已完成。
SQL>exit OracleDatabase10gEnterpriseEditionRelease10.2.0.1.0-Production WiththePartitioning,RealApplicationClusters,OLAPandDataMiningoptions
断开
C:\DocumentsandSettings\Administrator>setnls_lang=american_america.us7ascii C:\DocumentsandSettings\Administrator>sqlplustest/test@dmdb SQL*Plus:Release10.2.0.1.0-ProductiononMonJan2800:48:412008 Copyright(c)1982,2005,Oracle.Allrightsreserved. Connectedto: OracleDatabase10gEnterpriseEditionRelease10.2.0.1.0-Production WiththePartitioning,RealApplicationClusters,OLAPandDataMiningoptions SQL>insertintot1values(’中’); 1rowcreated.
抓获的网络包发现,在SQL提交给服务器之前已经转换了。OCI库认为提交过来的编码是US7ASCII,因此要将转换为服务器端的ZHS16GBK编码,然而“中”的编码即16进制D6 D0并不是有效的US7ASCII编码,所以ORACLE OCI就转为了转省值3F3F(US7ASCII是单字节Oracle字符集,会认为“中”字是两个字符,因此为有两个3F) 这就是“??”号的由来。
000000900000000000000000000000C81DFF001C……………. 000000A0696E7365727420696E746F2074312076insert.into.t1.v 000000B0616C7565732028273F3F272901000000alues.(’??’)…. 000000C001000000000000000000000000000000…………….
我们再看看将客户端NLS_LANG设置为simplified chinese_china.zhs16cgb231280会发生什么