1、關於字符集 html
字符集(也稱字元集,Character Set)就是字符編碼表(codepage),一個字符不論英文、中文、韓文等在計算機系統內存或硬盤中經過二進制的字節(Byte)保存,這個二進制的編碼就是字符編碼(也稱內碼),字符集就是字符與內碼的對應(映射)表。sql
字符集編碼(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)
2)
1、Oracle字符集
Oracle字符集包括兩部分。一部分是Server端數據庫運行實例(instance)的字符集,一部分是Oracle客戶端Client的字符集。
1,
在安裝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的客戶端安裝Oracle Client過程當中並無選項選擇Oracle Client的字符集,安裝完畢後在註冊表HKLOCAL_MACHINE\SOFTWARE\ORACLE\KEY_ORACLECLENT_HOME1\能夠找到NLS_LANG鍵,值爲當前OS的字符集。如簡體系統爲:ZHS16GBK,繁體系統爲:MSWIN950。可見,Oracle Client(如下簡稱NLS_LANG)在安裝過程當中選擇了OS的字符集做爲默認的NLS_LANG字符集。
a)
b)
c)
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.」
在安裝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這樣的字段中去?