提及Oracle字符集可以說是老生常,Oracle字符集問題的引起,主要是因為其的“亂碼”問題。而亂碼的產生主要是因為客戶端和服務器的實際應用的字符集不同,其進行字符集轉換而引起的。
不過很多提到了轉換,卻沒有提到這個轉換是在哪個階段和哪裡發生的?是在服務器向塊裡寫入數據的時候嗎?在客戶端還是在服務器端?
正確的答案是,普通字符串轉換發生在客戶端(具體來說是由OCI LIBRARY完成的),國家字符串經過兩次轉換,第一次發生在客戶端,第二次發生在服務器端。下面做個測試:
連接到:
- Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
- With the Partitioning, Real Application Clusters, OLAP and Data Mining options
- SQL> select * from nls_database_parameters where parameter like ‘%CHARACTERSET%’;
- PARAMETER VALUE
- ------------------------------ ------------------------------
- NLS_CHARACTERSET ZHS16GBK
- NLS_NCHAR_CHARACTERSET AL16UTF16
- SQL> create table t1(a varchar2(100));
表已創建。
- SQL>
- SQL> insert into t1 values (’中’);
已創建 1 行。
- SQL>
在本次連接中,我沒有設置NLS_LANG變量。則客戶端Oracle字符集為操作系統的缺省字符集ZHS16GBK。通過捕獲網絡包,可以發現客戶端傳送給客戶端的數據(不能上傳圖片,郁悶):
- 00000090 00 00 00 00 00 00 00 00 00 00 00 28 DB 00 01 1C ………..(….
- 000000A0 69 6E 73 65 72 74 20 69 6E 74 6F 20 74 31 20 76 insert.into.t1.v
- 000000B0 61 6C 75 65 73 20 28 27D6 D027 29 01 00 00 00 alues.(’..’)….
- 000000C0 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
注意紅色的部分,16進制D6 D0正是“中”字的GBK編碼。(關於怎麼獲取漢字的各種編碼,暫且略過,如有需要再交流)
現在我們退出SQLPLUS,設置環境變量NLS_LANG:
- SQL> rollback;
回退已完成。
- SQL> exit
- Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
- With the Partitioning, Real Application Clusters, OLAP and Data Mining options
斷開
- C:\Documents and Settings\Administrator>set nls_lang=american_america.us7ascii
- C:\Documents and Settings\Administrator>sqlplus test/test@dmdb
- SQL*Plus: Release 10.2.0.1.0 - Production on Mon Jan 28 00:48:41 2008
- Copyright (c) 1982, 2005, Oracle. All rights reserved.
- Connected to:
- Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
- With the Partitioning, Real Application Clusters, OLAP and Data Mining options
- SQL> insert into t1 values (’中’);
- 1 row created.
抓獲的網絡包發現,在SQL提交給服務器之前已經轉換了。OCI庫認為提交過來的編碼是US7ASCII,因此要將轉換為服務器端的ZHS16GBK編碼,然而“中”的編碼即16進制D6 D0並不是有效的US7ASCII編碼,所以ORACLE OCI就轉為了轉省值3F3F(US7ASCII是單字節Oracle字符集,會認為“中”字是兩個字符,因此為有兩個3F) 這就是“??”號的由來。
- 00000090 00 00 00 00 00 00 00 00 00 00 00 C8 1D FF 00 1C …………….
- 000000A0 69 6E 73 65 72 74 20 69 6E 74 6F 20 74 31 20 76 insert.into.t1.v
- 000000B0 61 6C 75 65 73 20 28 273F 3F27 29 01 00 00 00 alues.(’??’)….
- 000000C0 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
我們再看看將客戶端NLS_LANG設置為simplifIEd chinese_china.zhs16cgb231280會發生什麼以上的相關內容就是對再論Oracle字符集轉換的部分內容的介紹。