Oracle數據庫多語言文字存儲解決方案

 1、關於字符集 html

 

字符集(也稱字元集,Character Set)就是字符編碼表(codepage),一個字符不論英文、中文、韓文等在計算機系統內存或硬盤中經過二進制的字節(Byte)保存,這個二進制的編碼就是字符編碼(也稱內碼),字符集就是字符與內碼的對應(映射)表。sql

    由於多國語言的緣由,就出現了根據本國語言製做的字符集。如使用最普遍的ASCII編碼,由美國國家標準局(即ANSI)制定,適用於全部拉丁、英文字符。中國大陸使用GB2312,GBK,GB18030等字符集,這些字符集包含全部漢字字符的內碼,其中GBK,GB18030稱爲大字符集,對繁體中文也進行了編碼。香港、臺灣、澳門地區使用Big5編碼,Big5收錄了繁體中文(有些繁體與中國大陸繁體字符有差別)的編碼,不包含簡體中文的字符編碼。韓文使用euc-kr的字符集,韓文中也有不少漢字,因此字符集包括不少漢字字符的編碼。其餘如日文、俄文等都有本身國家制定的字符集,用來保證計算機系統能正確顯示本國的語言文字。不一樣語言的字符集不具備通用性,ASCII字符集沒有制定中文字符的編碼,GB2312沒有制定韓文字符的編碼,Big5沒有制定簡體中文字符的編碼,針對這種不兼容性,官方發佈了Unicode(進一步優化的UTF7,UTF8,UTF16等)字符集,對每一種語言的每一個字符制定了統一且惟一的內碼,知足跨語言、跨平臺的字符解碼和轉換處理。數據庫

 

字符集編碼(16進制)示例:session

 

字符/字符集oracle

GBKide

Euc-kr  工具

UTF8測試

UTF16  字體

物流優化

ce-ef ,c1-f7

da-aa,d7-b5

e7-89-a9,e6-b5-81

72-69,6d-41

삼성

bb-ef ,bc-ba

ec-82-bc,ec-84-b1

c0-bc,c1-31

 

注:

1)       字符「삼성」在韓文字符集Euc-kr中的編碼是bb-ef-bc-ba,在GBK字符集中是沒有「삼성」這兩個字符的,也就是說bb-ef-bc-ba在Euc-kr與GBK編碼對照表中是沒有記錄的,若是你硬是要GBK字符集來對「삼성」做出解釋(解碼),那GBK就用字符「?」(因韓文字符是兩個字節,因此使用全角?)代替,全角?的編碼在GBK中是a3-bf。

2)       漢字「物流」字符在Euc-kr中的編碼是da-aa,d7-b5,這說明韓文字符集中包含了部分漢字的編碼,固然這個編碼與GBK字符集中「物流」兩個字符的編碼(ce-ef ,c1-f7)是不一樣的,用GBK去解釋韓文字符集中「物流」兩個字符,顯示的結果確定不是「物流」兩個字符。一樣的,在GBK中不少繁體中文字符的編碼與BIG5中相同繁體中文的字符編碼也是不一樣的,例如你在簡體中文環境開發應用程序時,窗體控件使用繁體中文表示,可是在繁體OS運行應用程序,控件上的繁體中文變成了亂碼或?,緣由就是不一樣字符集一樣字符的編碼是不一樣的,解決這個問題的方法就是將應用程序使用unicode編碼保存,告訴操做系統使用unicode字符集對你的應用程序中的字符進行解碼。

 

    Windows操做系統(OS)的字符集:不一樣語言的OS的默認字符集是不同的。英文OS使用ASCII字符集做爲系統的字符集,簡體使用GB2312,繁體使用Big5(在VB.NET中,可用System.Text.Encoding.Default.EncodingName檢查OS的字符集)。Windows系統自己對系統默認的字符集有很好的支持,可是安裝在OS上的應用程序卻不必定這樣。例如,在一個簡體中文操做系統上安裝了韓文版的某個應用程序,這個應用程序在開發時使用的是euc-kr字符集編碼。由於OS默認的處理非Unicode程序的字符集是GB2312,在 GB2312字符集並未對任何韓文字符進行編碼,在GB2312內找不到任何一個韓文字符的內碼,找不到只能以「?」代替這個字符,對應的編碼變成了「?」的編碼,例如:「삼성」這兩個韓文字符在程序運行時顯示的是「?」。解決這個問題,有三種方法:1,該韓文應用程序使用Unicode編碼保存。在簡體環境運行時,OS使用Unicode字符集解碼,只要系統安裝韓文字體,就可正常顯示韓文;2,將OS處理非Unicode程序使用的字符集改成euc-kr,支持韓文應用程序的解碼(在control panel->Regional and Language Options 修改);3,安裝微軟的AppLocale工具,指定該韓文應用程序運行時使用euc-kr的字符集。

1、Oracle字符集

Oracle字符集包括兩部分。一部分是Server端數據庫運行實例(instance)的字符集,一部分是Oracle客戶端Client的字符集。

1,  數據庫實例的字符集(以Oracle 10g爲例)

 

  

在安裝Oracle數據庫過程當中,能夠選擇數據庫字符集。默認的是OS系統的字符集,如簡體中文系統是GB2312,繁體系統是BIG5。Oracle對於簡體系統的字符集使用ZHS16GBK,GBK是GB2312的超集,Oracle不識別GB2312,只認ZHS16GBK。此外,選擇Unicode做爲數據庫字符集,全部存儲數據都將以Unicode編碼存儲在數據庫中,不論字段類型是Number,varchar2,DateTime等。或者也能夠從字符集列表中選擇其餘字符集。注意到有個國家字符集的選項,並且國家字符集的選擇只有unicode,這是爲當數據庫字符集爲非Unicode時將數據存爲Unicode編碼時用到。好比:數據庫字符集爲ZHS16GBK,數據庫表mer_categ的一個字段爲S_merc_name,數據類型爲varchar2,存入中文字符「物流」,「物流」這兩個字符使用GBK字符集編碼存儲。經過「select dump(s_merc_name,16) from mer_categ」查詢該字段在數據庫中的內碼獲得結果是:ce,ef ,c1,f7,這與咱們在前面看到的示例表中這兩個字的內碼是一致的。如今,把S_merc_name這個字段類型改成nvarchar2,意味着這個字段數據的存儲將使用National Charset(國家字符集)——AL16UTF16的編碼存儲。一樣寫入「物流」兩個中文字符,再次查詢該數據在數據庫中的內碼結果是:e7,89,a9e6,b5,81,這就是咱們爲何使用nvarchar2做爲字段類型的緣由。回過頭來看,若是數據庫字符集選擇了Unicode,那表字段中使用nvarchar2已經沒什麼意義了,由於全部字段類型的數據都是使用Unicdoe編碼來保存的。

查詢數據庫字符集的sql指令是:

「select * from v$nls_parameters」

得出結果:

 

NLS_LANGUAGE

SIMPLIFIED CHINESE

NLS_TERRITORY

CHINA

NLS_CURRENCY

NLS_ISO_CURRENCY

CHINA

NLS_NUMERIC_CHARACTERS

.,

NLS_CALENDAR

GREGORIAN

NLS_DATE_FORMAT

DD-MON-RR

NLS_DATE_LANGUAGE

SIMPLIFIED CHINESE

NLS_CHARACTERSET

AL32UTF8

NLS_SORT

BINARY

NLS_TIME_FORMAT

HH.MI.SSXFF AM

NLS_TIMESTAMP_FORMAT

DD-MON-RR HH.MI.SSXFF AM

NLS_TIME_TZ_FORMAT

HH.MI.SSXFF AM TZR

NLS_TIMESTAMP_TZ_FORMAT

DD-MON-RR HH.MI.SSXFF AM TZR

NLS_DUAL_CURRENCY

NLS_NCHAR_CHARACTERSET

UTF8

NLS_COMP

BINARY

NLS_LENGTH_SEMANTICS

BYTE

NLS_NCHAR_CONV_EXCP

FALSE

 

 

其中NLS_CHARACTERSET 是數據庫的字符集;NLS_NCHAR_CHARACTERSET是國家字符集。

數據庫的字符集在創建數據庫的時候建立。目前也有方法轉換字符集,但兩個字符集的轉換仍是存在風險,形成數據丟失或錯誤,不建議使用。

 

2,  Oracle Client字符集(NLS_LANG)

在訪問Oracle的客戶端安裝Oracle Client過程當中並無選項選擇Oracle Client的字符集,安裝完畢後在註冊表HKLOCAL_MACHINE\SOFTWARE\ORACLE\KEY_ORACLECLENT_HOME1\能夠找到NLS_LANG鍵,值爲當前OS的字符集。如簡體系統爲:ZHS16GBK,繁體系統爲:MSWIN950。可見,Oracle Client(如下簡稱NLS_LANG)在安裝過程當中選擇了OS的字符集做爲默認的NLS_LANG字符集。

   設定NLS_LANG有三種方法:

a)      修改註冊表。將HKLOCAL_MACHINE\SOFTWARE\ORACLE\KEY_ORACLECLENT_HOME1\下NLS_LANG鍵值改成你要設定的字符集,如將SIMPLIFIED CHINESE_CHINA.ZHS16GBK改成:SIMPLIFIED CHINESE_CHINA.AL32UTF8。但這種作法彷佛無效,即便從新啓動機器後,也沒有生效,NLS_LANG仍使用當初安裝時的OS字符集。

b)      設定環境變量。在My Computer->Properties->Advanced->Environment Variables->System Variables 新增環境變量設置,如:Variable name=NLS_LANG,Variable Value= SIMPLIFIED CHINESE_CHINA.AL32UTF8。這樣NLS_LANG字符集爲UTF8,這個NLS_LANG優先序高於註冊表中的NLS。注意:環境變量設在系統變量中(System Vairables),而不是用戶變量(User Vairables)。

c)      在應用程序運行的Process Session中設定。在程序運行以前,先經過set NLS_LANG=進程Session的字符集。例如:

 

Echo %nls_lang%,在這個session中,已經設定NLS_LANG字符集爲ZHS16GBK。一樣,你也能夠新開一個CMD窗口,設定另外一種NLS_LANG字符集。這種在session中設定NLS_LANG的優先序高於系統環境變量NLS_LANG。註冊表NLS_LANG、系統環境變量NLS_LANG、Session NLS_LANG的優先序是:Session NLS_LANG > 系統環境變量NLS_LANG > 註冊表NLS_LANG。

3、Oracle數據庫如何存儲多國文字

首先,先看一個有趣的問題。咱們要保存的文字哪裏來?一種方法是在屏幕上輸入,輸入中文時,咱們打開本身習慣的輸入法,在應用程序給你提供的輸入框內輸入文字;還有一種方法是複製粘貼的方法,將文字從網頁、文檔中拷貝後再粘貼到輸入框中,那問題是:輸入框中的文字使用的字符集是什麼?有人說,是客戶端操做系統的默認字符集。好,個人操做系統是簡體中文,默認系統字符集是GB2312,在輸入框中輸入韓文字符「삼성」,若是這兩個韓文字符是用GB2312字符集解碼的,輸入框內應該顯示兩個「?」,由於GB2312字符集內沒有韓文字符只能用「?」代替。可是如今這兩個韓文字符在輸入框內正常的顯示,說明這兩個韓文的字符集是支持韓文字符的字符集,是韓文字符集「euc-kr」,仍是Unicode字符集?肉眼看不出來,也許查看一下內存中輸入框中文字的編碼能找到答案,喜歡鑽研的人能夠去看一看。表面來判斷,多是根據輸入法使用的字符集,輸入法使用什麼樣的字符集,輸入的文字就是用的什麼樣的字符集。從網頁上覆制粘貼過來的文字,應該跟網頁的字符集是一致的。其實,咱們不關心多國文字背後的字符集是用的本國字符集仍是unicode字符集,咱們關心的是多國文字如何在數據庫中被正確地存取。 

 

以oracle 10g R2 10.2.0.3 做爲測試數據庫版本,在數據庫創建一張表:

表名稱:mer_categ,商品類別表

 

 

字段

數據類型

長度

說明

S_merc_id

Varchar2

20

分類編號

S_merc_name

Varchar2

50

分類名稱

 

 

 

如今要新增一個分類編號爲01,分類名稱爲「삼성」的記錄。即便SQL的初學者也會寫出:

Insert into mer_catag values (‘01’,’삼성’);

這條SQL語句沒有錯,可是能不能把「삼성」正確的寫入數據庫,僅僅靠一條SQL語句是不夠的,在分析各類測試環境以前,咱們看一段官方關於SQL語句中字符串字符編碼的描述:

 

「Being part of a SQL or PL/SQL statement, the text of any literal, with or without the prefix N, is encoded in the same character set as the rest of the statement. On the client side, the statement is in the client character set, which is determined by the client character set defined in NLS_LANG, or specified in theOCIEnvNlsCreate() call, or predefined as UTF -16 in JDBC. On the server side the statement is in the database character set.」

 

 這段話告訴咱們:SQL語句提交到Server以前,SQL語句中的字符串部分(無論前面有沒有N’)做爲語句的一部分將被用NLS_LANG定義的字符集進行編碼。提交到Server端後,再被數據庫字符集編碼。能夠推出:若是數據庫字符集與客戶端NLS_LANG字符集一致,在Server端就沒有必要再一次編碼了。 

 

 

 

在安裝oracle數據庫時若是一直選擇「下一步」,數據庫字符集將默認使用操做系統字符集。如在簡體OS下,安裝Oracle Server,建立Oracle實例後,實例的數據庫字符集默認是ZHS16GBK;安裝客戶端後,NLS_LANG默認是ZHS16GBK字符集。GBK是漢字大字符集,處理漢字簡繁體均可以,因此平時使用相似:

Insert into mer_catag values (‘01’,’中國’);

Insert into mer_catag values (‘01’,’中國’);

都是正常的。可是當處理多國文字時,就不靈了。如:

Insert into mer_catag values (‘01’,’삼성’);

根據前面引用的官方描述,這條SQL語句不管是字符串部分仍是其餘部分,將先被NLS_LANG定義的字符集轉換,「삼성」將用GBK字符集轉換,前面講過,「삼성」轉換後的字符是「??」(兩個全角問號)。SQL語句變成了:

Insert into mer_catag values (‘01’,’??’);

在server端,因數據庫字符集與NLS_LANG一致,SQL語句再也不進行字符集轉換,執行SQL後,數據庫存儲了兩個全角「?」。用select dump(s_merc_name,16) from mer_categ驗證一下,獲得結果是:Typ=1 Len=4: a3,bf,a3,bf。「a3,bf」就是全角」?」的GBK編碼。兩個韓文字符被看成」?」存在了數據庫中,查詢出的固然也是」?」。因此,不對oracle字符集進行配置,不能正確處理多國文字的存儲。

客戶端的NLS能實現對多國文字進行正確字符集轉換的話,毫無疑問是選擇Unicode字符集。用set NLS_LANG =SIMPLE CHINESE.AL32UTF8能夠修改客戶端的NLS爲UTF8字符集,仍是以

Insert into mer_catag values (‘01’,’삼성’);

爲例,在客戶端,這條SQL語句使用UTF8字符集作了編碼轉換,韓文字符「삼성」的編碼轉爲:「ec-82-bc,ec-84-b1」(UTF8編碼)。下一步,關鍵看Server端的數據庫字符集了,若是數據庫字符集仍爲GBK,也就是說,「삼성 」要做UTF8->GBK的轉碼,能轉成功嗎?顯然不能夠,連個韓文字符又被全角「?」代替了。在server端,SQL語句被轉成了:

Insert into mer_catag values (‘01’,’??’);

數據庫又存了兩個全角「?」。怎麼辦?Server端能不能不做字符集轉換?前面提到,若是數據庫字符集與客戶端NLS_LANG字符集一致,在Server端就沒有必要再一次編碼了。好,那就在Server端將數據庫字符集設爲UTF8,韓文字符「삼성」就以UTF8的字符編碼存到數據庫裏了。驗證一下,select dump(s_merc_name,16) from mer_categ,結果是:Typ=1 Len=6: ec,82,bc,ec,84,b1。這個結果與前面看到的UTF8編碼是一致的。因此,當數據庫字符集與NLS_LANG(客戶端)都設爲UTF8字符集時,能夠解決多國文字正確存儲的問題。

 

彷佛將數據庫字符集與NLS_LANG都設爲UTF8的作法未獲得普遍應用,緣由是大多數的應用不存在使用多國語言的問題?沒作過相關調查。除此以外,使用UTF8存儲數據佔用更多的空間是事實存在的,一個漢字字符UTF8使用三個字節存儲,而在GBK中使用兩個字節,用GBK做爲數據庫字符集存儲漢字更經濟實惠些。最後,任何存入數據庫的數據都先通過UTF8的轉碼再存儲,必然帶來效能的損失。那能不能區別對待存儲的數據呢?例如只針對存在多國字符的數據使用Unicode字符集存儲呢?這就是國家字符集(National Chartset)存在的緣由。國家字符集是那些存放在NCHAR,NVARCHAR2,NCLOB字段中的數據的Unicode字符編碼集,Oracle 10g使用AL16UTF16字符集做爲默認的數據庫國家字符集。如何將多國文字存儲到NVARCHAR2這樣的字段中去?

相關文章
相關標籤/搜索