選擇ORACLE數據庫字符集

如何選擇數據庫的字符集是一個有爭議的話題,字符集本身涉及的範圍很廣,它與應用程序、客戶的本地環境、操作系統、服務器等關係很密切,因此要做出合適的 選擇,需要明白這些因素之間的關係。另外對字符集的基本概念,ORACLE數據庫字符集的一些知識也需要了解。
    隨着國內的軟件產品逐步走向海外,對於多語言的支持已經成爲軟件的一個基本要求,採用UNICODE標準也逐漸成爲通用的設計方案,此時ORACLE數據庫的字符集應該如何選擇?很多人都有自己的見解,在網上也可以看到很多關於字符集的文章。這些文章有很多精華值得去學習,但是另一方面還存在一些錯誤,尤其對UNICODE,存在一些概念不清的地方。
    數據庫字符集的選擇並不存在絕對意義上的正確或錯誤,每種字符集都有它適用的環境。對於我們來說,瞭解得越多,越能幫助自己做出適當地選擇,而且可以採取措施去主動防範或規避可能出現的問題。反之,如果數據庫字符集選擇不恰當,會給後面的工作帶來很多的麻煩,需要花費很多時間和精力去解決問題,有些問題甚至會影響到客戶的業務使用。本文希望可以給大家提供一些相對全面的知識,方便大家瞭解數據庫字符集的相關概念,因此有些繁瑣,請大家見諒。另外由於個人的侷限,有何不妥之處還請大家不吝指正。
下面我們由淺入深,先由概念入手,再給出幾種常用的字符集設置建議,對一些可能遇到的問題做出分析,最後給出自己的建議。
    1、字符集的一些基本知識
    講到數據庫的字符集設置,首先需要對字符集的知識有些瞭解。以下是字符集的基本知識介紹:由於計算機只能存儲使用二進制數據,因此對於一些字符或符號,需要對它們進行編碼,用編碼後的數值來表示這些字符。對於一組符號的編碼集合就是字符集。
    字符集有很多種,最初的字符集是ASCII,它用一個字節中的7位來表示128個字符,第8位沒有使用。它包括大小寫字母、數字0-9、標點符號、非打印字符(換行符、製表符等4個)以及控制字符(退格、響鈴等)等。由於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,可以通過GB18030升級包升級到GB18030。不過GB18030相對GBK增加的字符,普通人是很難用到的,因此GBK還是我們目前最常用的簡體中文字符集。
    由於編碼方案太多且彼此之間不兼容,存在互相之間存在衝突的情況,即對於同一個編碼數值,在兩種不同的編碼方案中代表的是兩個不同的字符。這樣對於一些WEB應用來說,由於多種語言文字的同時使用及存儲,需要採用一種統一的字符集。爲此,國際標準化組織(ISO)制定了ISO 10646碼錶,而Unicode協會制定了Unicode規範,這兩個體系剛開始時是獨立建立的,在1991年,雙方都認識到世界不需要兩個不兼容的字符集。於是它們開始合併雙方的工作成果,併爲創立一個單一編碼表而協同工作。從Unicode2.0開始,Unicode項目採用了與ISO 10646-1相同的字庫和字碼。目前兩個項目仍都存在,並獨立地公佈各自的標準。Unicode協會現在的最新版本是2006年的Unicode 5.0。ISO的最新標準是10646-3:2003。下面簡單介紹一下幾種常見的編碼方式:
    UCS(Universal Character Set)是按ISO-10646定義的字符集,有兩種最常用編碼方式: UCS-2和UCS-4。
    UCS-2:使用0-65535之間的數表示一個unicode字符。UCS-2無法表示所有的unicode字符,只能表示其前65536個字符(稱爲Basic Multilingual Plane,BMP)。我們一般經常使用的UNICODE碼就是指這個編碼方案。
    UCS-4:使用0-FFFFFFFF之間的數表示一個unicode字符,但爲了和unicode體系兼容(unicode體系是一個20bit系統),ISO-10646表示所有定義的字符將不超過10FFFF。UCS-4可以表示所有的unicode字符。
Unix 下使用 UCS-2/UCS-4會導致非常嚴重的問題,因爲有一些特殊的字符, 比如 '/0' 或 '/', 它們在文件名和其他C的庫函數參數裏都有特別的含義,C語言使用'/0'作爲字符串結尾,而Unicode裏恰恰有很多字符都有一個字節爲0,這樣一來,C語言的字符串函數將無法正常處理Unicode,除非把世界上所有用C寫的程序以及他們所用的函數庫全部換掉 。
    爲了解決這個問題,於是產生了將Unicode編碼規則和計算機的實際編碼對應起來的一個規則,UTF,英文爲UCS Transformation Format,即UCS轉換格式,目前常用的有UTF-8、UTF-16、UTF-32三種。(還有UTF-7在此就不介紹了)。正如名字所示,它們分別使用8位、16位、32位比特對UCS進行編碼。
    UTF-8:一種變長的Unicode編碼方式,使用1到4個字節表示一個字符。這種方式的最大好處,是UTF-8保留了ASCII字符的編碼作爲它的一部分,因此在ASCII表示的128個字符在UTF-8的編碼沒有變化,它的兼容性比較好。UTF-8在目前WEB應用上使用很廣泛。
    UTF-16:一種變長Unicode編碼方式,使用兩個或者四個字節表示一個字符。這種編碼方式比較節省空間,因爲它把最常使用的字符都用兩個字節來表示,而那些不常用的字節則用兩個或四個字節來表示。但對於英文字符來說,它要用兩個字節來編碼。
    UTF-32:一種固定長度的Unicode編碼方式,使用四個字節表示一個字符,它適用在內存很充足,需要定長的編碼場合。
    2、ORACLE數據庫的字符集
    ORACLE的字符集名字一般由以下部分組成:語言或區域、表示一個字符的比特位數、標準字符集名稱(可選項,S或C,表示服務器或客戶端)。ORACLE字符集UTF8與UTFE不符合此規定,其它基本都是這種格式。
    對於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的版本。
 
    字符集 對應的數據庫版本範圍 Unicode的版本    
 

字符集
對應的數據庫版本範圍
Unicode的版本
AL24UTFFSS
7.2-8.1
1.1
UTF8
8.0-10g
2.1 (8.0-8.1.6)
3.0 (8.1.7-10g)
UTFE
8.0-10g
2.1 (8.0-8.1.6)
3.0 (8.1.7-10g)
AL32UTF8
9.0-10g
3.0 (9.0)
3.1 (9.2)
3.2 (10.1)
4.01(10.2)
AL16UTF16
9.0-10g
3.0 (9.0)
3.1 (9.2)
3.2 (10.1)
4.01(10.2)


    AL24UTFFSS:是ORACLE第一種支持Unicode的字符集,從7.2版本開始使用,但是它支持的Unicode版本爲1.1,因此從9i開始就不支持此字符集了。
    UTF8:是ORACLE從ORACLE8開始使用的屬於UTF-8編碼的字符集,從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作爲子集的編碼方案。
   從以上幾種字符集的介紹來看,Unicode字符集一般使用UTF8和AL32UTF8。如果數據庫版本都在9i及其以上,不需要考慮ORACLE8的數據庫,建議使用AL32UTF8字符集,它採用的Unicode標準要比UTF8採用的Unicode標準更新,支持的字符也更多一些。如果要考慮ORACLE8數據庫,建議使用UTF8字符集,它的兼容性好,在ORACLE8及8I數據庫上使用AL32UTF8字符集容易出現問題。
    3、如何選擇合適的數據庫字符集
    前面我們介紹了字符集的一些概念,並對ORACLE數據庫的常用幾個字符集有了一些瞭解,下面就具體對數據庫字符集的選擇闡述一些個人的觀點:
    3.1、數據庫需要存儲的數據類型是字符集選擇的首要考慮目標。
    由於數據庫的主要功能在於存儲數據,因此要保證數據的正確性。採用何種數據庫字符集需要看存儲數據是何種類型的。對於只存儲英文信息的數據庫等來說,一般採用US7ASCII或WE8ISO8859P1等單字節的字符集就比較合適,在性能和空間上也是最優,如果採用ZHS16GBK編碼,雖然可以使用,但從數據庫字符集本身的含義來說,屬於不恰當的選擇。同樣,存儲了中文信息的數據庫,如果採用單字節的字符集,也是不合適的。在這種情況下,數據庫的字符集雖然是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、數據庫字符集設置不當引起的亂碼:
    這種錯誤是由於數據庫字符集選擇錯誤而引起的。我們前面講過,由於每種語言文字都有一些自己特殊的字符,甚至一些字符的寫法都有不同的講究,因此即使對於歐美國家來說,也不是可以隨便通用的。像西歐的字符集標準ISO 8859-1是8位編碼,它就有自己的一些特殊符號,這些字符在US7ASCII編碼中找不到對應的編碼值。如果需要使用這些特殊符號,就必須選用本地字符集或者是它的超集的字符集。如果選用的字符集兩者都不是,那麼在數據庫存儲的數據實際編碼和數據庫字符集的設置就產生了不一致,很容易產生亂碼。
例如:一個存儲簡體中文字符的數據庫,它的字符集選用了US7ASCII,當它的客戶端NLS_LANG也選用US7ASCII時,這個系統單獨使用是沒有問題的,因爲兩者設置一致,因此ORACLE不會進行字符集的轉換,客戶輸入的GBK碼被直接在數據庫中存儲起來,當查詢數據時,實際客戶端取出來的數據也是GBK的編碼,因此顯示也是正常的。但當其它的系統需要從這個數據庫取數據,或者它的數據要EXP出來,IMP到其它數據庫時,問題就會開始出現了。其它系統的字符集一般是ZHS16GBK,或者其它系統客戶端的NLS_LANG設置爲ZHS16GBK,此時必然會產生字符集的轉換。雖然數據庫字符集設置爲US7ASCII,但我們知道,實際存儲的數據編碼是ZHS16GBK的。可惜ORACLE不會知道,它會把存儲的ZHS16GBK編碼數據當作US7ASCII編碼的數據,按照US7ASCII轉換成ZHS16GBK的轉換算法進行轉換,可以想象,這種情況下,亂碼的產生是必然的。
結論是:如果要選擇一個非本地環境的數據庫字符集,在不需要考慮和其它系統的數據接口和數據交換的情況下,或者你有面對這種麻煩的心理準備的話,那麼這種選擇是可行的,但是別忘了數據庫字符集一定要和客戶端的NLS_LANG保持一致。
    4.2.2、數據庫字符集與客戶端NLS_LANG設置不同引起的亂碼:
    由於ORACLE提供了字符集的轉換功能,因此數據庫字符集與客戶端NLS_LANG設置不同是可以接受的,前提條件是數據庫的字符集必須是客戶端NLS_LANG設置字符集的超集,那麼由於客戶端使用的字符是屬於數據庫字符集中的一部分,因此不會產生轉換時數據丟失及亂碼的情況。
    例如:對於一個需要存儲簡體文信息的數據庫來說,它的字符集設置和客戶端NLS_LANG設置一般可以使用ZHS16GBK編碼。但是如果數據庫字符集選用了UTF8的話,也是可以的,因爲ZHS16GBK編碼屬於UTF8的子集。ORACLE在數據庫與客戶端進行數據交換時自動進行編碼的轉換,在數據庫中實際存儲的也是UTF8編碼的數據。此時其它數據庫和此數據庫也可以正常的進行數據交換,因爲ORACLE會自動進行數據的轉換。在實際使用中,遇到過繁體XP的字符集ZHT16MSWIN950轉換成AL32UTF8字符集時,一些特殊的字符和個別冷僻的漢字會變成亂碼。後來證實是XP需要安裝一個字庫補丁軟件,最後順利解決此問題。
    結論:對於數據庫字符集爲UTF8,而客戶端採用本地字符集的情況,最好進行測試驗證,因爲UNICODE標準本身發展很快,一些客戶端的操作系統對UNICODE標準支持的力度不一致,有些操作系統支持不好,有些特殊字符在轉換後會產生亂碼。由於這個話題已經超出了本文的範疇,在此就不詳細討論了。
    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等,它們沒有提供編碼轉換功能,因此在客戶端直接輸入或查詢數據往往都會遇到亂碼的問題,必須由應用程序或一些工具去做編碼的轉換,才能保證正常的使用。比如SQL*PLUS遇到的問題,我們在4.1節中已經進行了詳細的論述。
    5、最後的結論及建議:
    以上不厭其煩的列舉了種種因爲選用了不恰當的數據庫字符集而出現的問題,最後總結歸納起來,以下幾點就是我個人的建議了:
    1) 一般情況下,建議優先考慮客戶的本地化環境,選用本地通用的字符集作爲數據庫的字符集和客戶端NLS_LANG的設置,使得數據庫、客戶端NLS_LANG、客戶端操作系統三者的字符集可以完全兼容,這樣出現的問題和麻煩最少。就簡體中文而言,目前最常用的字符集是ZHS16GBK,建議大家選用。
    2) 如果系統需要支持多語言,採用了UNICODE標準,那麼ORACLE數據庫字符集在版本9以上可以選用AL32UTF8,如果涉及到ORACLE8及8i的數據庫,字符集可以選用UTF8。
    3) 如果應用程序完全支持UNICODE,可以根據客戶的本地化環境自動轉換編碼,則客戶端的NLS_LANG可以設置成和數據庫服務器端字符集完全一致。如果應用程序不能自動進行編碼的轉換或需要在客戶端進行一些管理維護活動,則建議把客戶端的NLS_LANG設置成本地環境使用的字符集,由ORACLE來進行編碼的轉換工作。此時需要對客戶端的操作系統進行驗證測試,因爲目前各個操作系統對UNICODE標準支持的程度不同,有時會出現一些特殊字符轉換不正常的情況。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章