Oracle 字元

ForTechnology發表於2011-08-17
1、字符集的一些基本知識
    字符集有很多種,最初的字符集是ASCII,由於ASCII支援的字元很有限,因此隨後又出現了很多的編碼方案,這些編碼方案大部分都是包括了ASCII的。EBCDIC編碼是另一個比較基本的編碼,它的部分字元采用了和ASCII不同的編碼值,因此兩者是不相容的基本編碼方案。採用EBCDIC編碼的比較少,目前主要是IBM 的系統採用,如AS400及S390系統,大部分的系統都是基於ASCII編碼的。
    由於亞洲國家的字符集相對複雜一些,因此一般都使用了兩個及以上的位元組進行編碼的方案。對於簡體中文,GB2312碼是國家1981年實施的編碼標準,通行於大陸。新加坡等地也使用此編碼。GBK編碼是GB2312碼的擴充套件,是1995年釋出的指導性規範,它在字彙一級支援 ISO/IEC 10646-1 和GB 13000-1 的全部中日韓 (CJK) 漢字(20902字)。目前最新的漢字字符集是2000年的GB18030,它是取代GBK1.0的正式國家標準。該標準收錄了27484個漢字,同時還收錄了藏文、蒙文、維吾爾文等主要的少數民族文字。目前簡體WINDOWS的預設內碼還是GBK。
    由於編碼方案太多且彼此之間不相容,存在互相之間存在衝突的情況,即對於同一個編碼數值,在兩種不同的編碼方案中代表的是兩個不同的字元。這樣對於一些WEB應用來說,由於多種語言文字的同時使用及儲存,需要採用一種統一的字符集。為此,國際標準化組織(ISO)制定了ISO 10646碼錶,而Unicode協會制定了Unicode規範,這兩個體系剛開始時是獨立建立的,在1991年,雙方都認識到世界不需要兩個不相容的字符集。於是它們開始合併雙方的工作成果,併為創立一個單一編碼表而協同工作。從Unicode2.0開始,Unicode專案採用了與ISO 10646-1相同的字型檔和字碼。目前兩個專案仍都存在,並獨立地公佈各自的標準。Unicode協會現在的最新版本是2006年的Unicode 5.0。
2、ORACLE資料庫的字符集
      ORACLE的字符集名字一般由以下部分組成:語言或區域、表示一個字元的位元位數、標準字符集名稱(可選項,S或C,表示伺服器或客戶端)。ORACLE字符集UTF8與UTFE不符合此規定,其它基本都是這種格式。
set nls_lang=AMERICAN_AMERICA.UTF8
set nls_lang=SIMPLIFIED CHINESE_AMERICA.UTF8    

對於US7ASCII,表示區域是US,用7個位元位表示一個字元,標準的字符集名稱為ASCII。
對於中文字符集ZHS16GBK,表示簡體中文(ZHT為繁體中文),一個字元需要16位位元,標準的字符集名稱為GBK。而ZHS16CGB231280表示簡體中文,一個字元需要16位位元,標準的字符集名稱為GB231280,屬於我們前面提過的1981年釋出的GB2312-80標準。雖然我們說,GBK編碼標準是GB2312編碼標準的擴充套件,但是資料庫字符集ZHS16GBK與ZHS16CGB231280之間卻不是嚴格的超集與子集的關係,主要是有些漢字的編碼在兩個字符集中的數值是不同的,因此它們進行字符集轉換時會出現問題。
在本文中,有時候使用的是標準字符集名稱,有時候又需要使用ORACLE字符集的名稱,因此希望大家明白兩者之間的對應關係。
ORACLE資料庫有國家字符集(national character set)與資料庫字符集(database character set)之分。兩者都是在建立資料庫時需要設定的。國家字符集主要是用於NCHAR、NVARCHAR、NCLOB型別的欄位資料,而資料庫字符集使用很廣泛,它用於:CHAR、VARCHAR、CLOB、LONG型別的欄位資料;表名、列名、PL/SQL中的變數名;輸入及儲存在資料庫的SQL和PL/SQL的原始碼。
ORACLE支援的Unicode字符集有以下幾種,下面的列表給出了字符集的名稱、對應的資料庫版本範圍、採用的Unicode的版本。
AL24UTFFSS:是ORACLE第一種支援Unicode的字符集,從7.2版本開始使用,但是它支援的Unicode版本為1.1,因此從9i開始就不支援此字符集了。
UTF8:是ORACLE從ORACLE8開始使用的屬於
的字符集,從ORACLE8.0到ORACLE8.16,Unicode版本為2.1,而ORACLE817到10g,採用的Unicode標、為3.0
UTFE:用於EBCDIC碼平臺上的資料庫Unicode字符集。因此它屬於專用系統使用的字符集,其它屬性與UTF8基本相同。
AL32UTF8:是從ORACLE9開始使用的屬於UTF-8編碼的字符集,與UTF8相比,它採用的Unicode版本更新,在10g版本中使用的是Unicode 4.01標準,而UTF8因為相容性的考慮,在10g版本中用的是Unicode 3.0標準。
AL16UTF16:是ORACLE第一種採用UTF-16編碼方式的字符集,從ORACLE9開始使用,是作為預設的國家字符集使用,它不能被用作資料庫的字符集。這是因為資料庫的字符集決定了SQL與PL/SQL原始碼的編碼方式,對於UTF-16這種使用固定的兩個位元組來表示英文字母的編碼方案來說,確實不適於用作資料庫的字符集,ORACLE目前採用的資料庫字符集都是基於ASCII或EBCDID作為子集的編碼方案。
3、如何選擇合適的資料庫字符集
3.1、資料庫需要儲存的資料型別是字符集選擇的首要考慮目標。
    對於只儲存英文資訊的資料庫等來說,一般採用US7ASCII或WE8ISO8859P1等單位元組的字符集就比較合適,在效能和空間上也是最優,
    同樣,儲存了中文資訊的資料庫,如果採用單位元組的字符集,也是不合適的。在這種情況下,資料庫的字符集雖然是US7ASCII或WE8ISO8859P1編碼,但裡面儲存的資料編碼實際上卻是另外的編碼格式,這種不一致的情況很容易引起問題,建議不要這樣使用。ORACLE提供了很多種類的字符集供客戶選擇,就是要滿足各種文字不同的編碼需要。

3.2、字符集的選擇需要優先考慮應用程式的需要。
    目前出於國際化的需要,軟體需要可以對不同的語言文字進行處理,尤其一個系統中需要容納多種語言文字的時候,一般都會採用Unicode這樣的通用解決方案,即使會有一些空間和執行效率的損失也是值得的。此時資料庫字符集建議可以採用AL32UTF8或UTF8編碼,一種比較理想的模式就是由程式負責編碼格式的轉換,而資料庫只提供一個透明的資料儲存,
    客戶在應用程式中輸入資料,此時資料的編碼格式是由客戶作業系統的區域及語言設定決定的,如在簡體中文XP的環境下,輸入的中文編碼屬於GBK編碼。在客戶輸入結束後,程式首先判斷客戶的本地環境,並把編碼轉換成UNICODE,並通過NET傳送到伺服器端。由於客戶端與伺服器資料庫的字符集均為UTF8格式,ORACLE在傳送過程中不會進行字元轉換,直接把資料按UTF8格式儲存到資料庫中。查詢時是一個反向的過程,應用程式從資料庫中取出UTF8編碼的資料,再由應用程式根據客戶的本地環境,把UTF8編碼的資料轉換成客戶本地的編碼格式,最後把結果資料顯示給客戶。此方案的關鍵在於應用程式要能很好的支援UNICODE編碼,編碼的轉換由應用程式來負責,資料庫只是提供了一個資料儲存功能。
    對於部分程式來說,由於對UNICODE支援不夠,沒有提供編碼的轉換功能,則可以使用ORACLE提供的字符集轉換功能來實現同樣的目的。客戶在應用程式中輸入資料,此時資料的編碼格式是由客戶作業系統的區域及語言設定決定的,如在簡體中文XP的環境下,輸入的中文編碼屬於GBK編碼。在客戶輸入結束後,程式直接把資料並通過NET傳送到伺服器端。由於客戶端與伺服器資料庫的字符集不一致,因此ORACLE會把客戶端的編碼轉換成UTF8格式,再把資料按UTF8格式儲存到資料庫中。這種方案的優點就是程式可以不用支援UNICODE,由ORACLE資料庫自動進行轉換。由於資料庫的字符集為UTF8,是其它字符集的超集,因此在轉換過程中不會發生資料丟失的情況。對於英文的字元符號,在UTF8中使用單位元組儲存,轉換的工作量很小,可以忽略,而對於一些亞洲字符集,在UTF8中一般需要兩到三個位元組儲存,需要的資料庫空間增加,而且轉換的工作量也相對大一些,效能會有一些損失。

4、與字符集相關的問題分析
4.1、在UTF8環境下執行SQL語句報錯的問題:
    SQL*PLUS工具不提供編碼自動轉換的功能,當資料庫字符集為UTF8,客戶端的NLS_LANG如果也是UTF8,那麼在SQL*PLUS中執行SQL語句時,語句全是英文,不會出現問題,如果語句包含了中文或其它一些特殊字元,SQL語句執行時就會報錯。對於返回的含中文的結果,SQL*PLUS也會顯示亂碼。造成此錯誤的原因在於當SQL語句中包含漢字等一些特殊字元時,由於這些字元的編碼屬於GBK,ORACLE沒有進行字元轉換,而是直接把SQL語句送到伺服器上進行解析。此時伺服器的字符集是UTF8,因此它按UTF8編碼格式對SQL語句中GBK編碼的字元解析時就會產生錯誤。如果把客戶端的NLS_LANG設定為本地環境的字符集,如ZHS16GBK,此時可以直接在SQL*PLUS中輸入包含中文的SQL語句,ORACLE在把SQL語句提交到伺服器時會自動轉換成UTF8編碼格式,因此SQL語句可以正常執行。對於英文字母,由於它在UTF8中的編碼數值採用的還是ASCII的編碼數值,因此英文字母可以直接使用而不需要轉換,這就是如果SQL語句或輸出結果全是英文時不會出現錯誤的原因。正確的做法是先把需要執行的SQL做成指令碼檔案,用程式碼轉換工具把它轉換成UTF8編碼格式的檔案,(注意!XP中的記事本是提供了程式碼轉換功能的,可以在儲存檔案或選擇檔案另存為的時候,彈出的對話方塊最後一項,編碼,選擇UTF8,再儲存,即可把檔案轉換成UTF8編碼格式)。完成後用IE開啟這個指令碼,選擇編碼-》UTF8,觀察此時SQL指令碼是否含有亂碼或“?”符號。如果沒有,說明編碼格式已經是UTF8了,此時在SQL*PLUS中執行這個指令碼就不會產生錯誤了。執行結束後,輸出的結果中如果包含中文,需要把結果SPOOL輸出到一個檔案中,然後用程式碼轉換工具把這個結果檔案由UTF8轉換成本地編碼格式,再用寫字板開啟,才能看到正常顯示的漢字。由於IE具有程式碼轉換功能,因此也可以不用程式碼轉換工具,直接在IE中開啟輸出的結果檔案,選擇UTF8編碼,也能正常顯示含中文的結果檔案。

4.2、資料庫出現亂碼的問題:
    資料庫出現亂碼的問題主要和客戶的本地化環境,客戶端NLS_LANG設定,伺服器端的資料庫字符集設定這三者有關,如果它們的設定不一致或者某個設定錯誤,就會很容易出現亂碼,下面我們簡要介紹以下幾種情況:
4.2.1、資料庫字符集設定不當引起的亂碼:
    例如:一個儲存簡體中文字元的資料庫,它的字符集選用了US7ASCII,當它的客戶端NLS_LANG也選用US7ASCII時,這個系統單獨使用是沒有問題的,因為兩者設定一致,因此ORACLE不會進行字符集的轉換,客戶輸入的GBK碼被直接在資料庫中儲存起來,當查詢資料時,實際客戶端取出來的資料也是GBK的編碼,因此顯示也是正常的。但當其它的系統需要從這個資料庫取資料,或者它的資料要EXP出來,IMP到其它資料庫時,問題就會開始出現了。其它系統的字符集一般是ZHS16GBK,或者其它系統客戶端的NLS_LANG設定為ZHS16GBK,此時必然會產生字符集的轉換。雖然資料庫字符集設定為US7ASCII,但我們知道,實際儲存的資料編碼是ZHS16GBK的。可惜ORACLE不會知道,它會把儲存的ZHS16GBK編碼資料當作US7ASCII編碼的資料,按照US7ASCII轉換成ZHS16GBK的轉換演算法進行轉換,可以想象,這種情況下,亂碼的產生是必然的。

4.2.2、資料庫字符集與客戶端NLS_LANG設定不同引起的亂碼:
    例如:對於一個需要儲存簡體文資訊的資料庫來說,它的字符集設定和客戶端NLS_LANG設定一般可以使用ZHS16GBK編碼。但是如果資料庫字符集選用了UTF8的話,也是可以的,因為ZHS16GBK編碼屬於UTF8的子集。ORACLE在資料庫與客戶端進行資料交換時自動進行編碼的轉換,在資料庫中實際儲存的也是UTF8編碼的資料。此時其它資料庫和此資料庫也可以正常的進行資料交換,因為ORACLE會自動進行資料的轉換。在實際使用中,遇到過繁體XP的字符集ZHT16MSWIN950轉換成AL32UTF8字符集時,一些特殊的字元和個別冷僻的漢字會變成亂碼。後來證實是XP需要安裝一個字型檔補丁軟體,最後順利解決此問題。

4.2.3、客戶端NLS_LANG與本地化環境不同引起的亂碼:
    一般情況下,客戶端NLS_LANG與本地化環境採用了不同的字符集會出現亂碼,除非本地化環境的字符集是客戶端NLS_LANG設定字符集的子集。如果把客戶端NLS_LANG設定為UTF8就屬於這種情況,由於目前還沒有可以直接使用UNICODE字符集的作業系統,因此客戶本地化環境使用的字符集只能是某種語言支援的字符集,它屬於UTF8的子集。下面我們就著重討論這種情況。
    雖然目前WINDOWS的核心是支援UNICODE的,但是WINDOWS並不支援直接顯示UNICODE編碼的字元,而且它並不知道目前的字元采用了何種字符集,所以預設情況下,它使用預設的內碼表來解釋字元。因此,對於其它型別的編碼,需要先進行轉換,變成系統目前的預設內碼表支援的字符集才能正常使用。
    WINDOWS中的預設內碼表是由控制皮膚設定中的語言及區域的選擇所決定的,屬於客戶本地化的環境設定。簡體中文WINDOWS的字元編碼就是GBK,它的預設內碼表是936。對於其它非WINDOWS的作業系統,我們可以把它們目前預設使用的字符集作為使用者的本地化環境設定。另外,我們使用的大部分工具,如寫字板,SQL*PLUS等,它們沒有提供編碼轉換功能,因此在客戶端直接輸入或查詢資料往往都會遇到亂碼的問題,必須由應用程式或一些工具去做編碼的轉換,才能保證正常的使用。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25897606/viewspace-705150/,如需轉載,請註明出處,否則將追究法律責任。

相關文章