開篇:因為要定位一個 關於dmp文件導入的亂碼問題, 於是乎我開始了漫長了 Oracle字符集搜索之路,網上關於講解oracle字符集的文章多得數不勝數,但轉載的這篇文章確是我最最喜歡的,圖文並茂,恰當的例子通俗易懂,對於我這種小菜最最適合不過。
本文轉自:http://blog.163.com/jiankun_liu/blog/static/1863927762013698175289/
原文標題:Oracle_字符集問題(數據庫與客戶端字符集關聯關系)
//時間:2013-07-07
//作者:shm
//描述:本文主要記錄了Oracle數據庫的字符集問題,也涉及作為服務器操作系統的CentOS或者Windows的字符集與Oracle字符集之間的關聯關系。
Oracle的字符集,這個問題的提出是因為兩個原因:一是工作中遇到一個DMP文件需要恢復到數據庫中去,而這個DMP文件的字符集是US7ASCII,第二個原因是一直在學習CentOS,在這個系統上安裝Oracle已經能成功,但是被中文英文系統字符集等問題搞得有點頭大。所以又回頭翻了翻蓋國強的書、看了看尚觀的視頻,終於有點心得,所以就寫下了這篇算是筆記的文章。
Oracle數據庫的字符集問題不算是大問題,但也是一個頭疼的問題。這是因為有這么三個原因:一是Oracle數據庫在安裝時指定好字符集之后一般不能更改,二是字符集問題涉及服務器與客戶端之間的存取問題,三是Oracle數據庫遷移時也會跟字符集非常相關。
首先,要說清楚Oracle字符集的相關問題,則要先理清數據庫運行過程中的架構以及在這個架構中的字符集設置及這些設置之間的關聯關系。
先畫一張圖看一下:
在這個圖中,為了說明問題,我們將服務器與客戶端分開,客戶端用應用程序比如sqlplus或者PL/SQL與服務端相連。
服務端有兩個字符集:服務端操作系統字符集(4)、服務端數據庫字符集(1);
客戶端有一個字符集:客戶端操作系統字符集(2);
客戶端有一個參數:操作系統參數NLS_LANG(3)。
這三個字符集與一個參數中,有一個字符集對整個架構的運行沒有影響,它就是服務端操作系統字符集(4),所以這個字符集將不再出現在我們的討論過程中。
為什么這個服務端操作系統字符集沒有用呢?這是因為Oracle在存取字符時與客戶端進行字符集確認與轉碼的過程是由Oracle數據庫自身完成的,不需要經過Oracle數據庫所在的服務器的幫助。具體的是怎么回事用以下例子說明一下。
比如在Oracle數據庫中有一個表,用如下語句創建:
create table test(name varchar2(10));
為了說明問題假定有這樣的一個環境:服務器端Oracle數據庫的字符集是UTF8,客戶端操作系統字符集是ZHS16GBK,客戶端NLS_LANG參數設置為ZHS16GBK。
那么從客戶端應用程序(比如sqlplus)發出這樣一條命令:
insert into test (name) values('中國');
首先,這里有一個字符串“中國”,客戶端操作系統用ZHS16GBK對它進行編碼,比如編成“167219”,並把它交給sqlplus程序,然后把它發送給Oracle數據庫。
接着,Oracle數據庫收到一串編碼“167219”,不是直接往數據庫里一扔就完事的,它要問客戶端操作系統:“請問你給我的這串代碼是用什么格式編碼的啊?”客戶端操作系統怎么回答?它會這么回答:“編碼格式請參照參數NLS_LANG”。Oracle數據庫一看,NLS_LANG='ZHS16GBK',這個編碼格式與Oracle數據庫自身的編碼格式“UTF8”不一樣,然后就是Oracle數據庫發揮自己專長的地方了,為什么呢?因為Oracle數據庫有它自己的編碼表,而且不是一張而是好多張編碼表,它可以根據編碼表對編碼進行翻譯和轉碼。這就好比Oracle數據庫是一個翻譯,它會好幾國語言,牛人一個。像上面的這個情況,Oracle會把“167219”這串代碼拿過來,根據參數NLS_LANG查ZHS16GBK編碼表,找到對應這串代碼的字符“中國”,然后再到UTF8編碼表中查“中國”對應的編碼,比如查到的結果是“3224678”。
最后,將轉碼之后的編碼“3224678”存放到Oracle數據庫中去。
為了進一步說明問題,我們再執行一條語句:
select name from test;
首先,Oracle數據庫會從數據庫中取出一串代碼“3224678”。
接着,Oracle數據庫不是直接把這串代碼交給sqlplus程序,它會多問一句:“代碼串我是取出來了,它是UTF8編碼格式的,請問sqlplus,你希望要什么編碼格式的?”,sqlplus仍然會很爽快地告訴Oracle數據庫:“編碼格式請繼續參照參數NLS_LANG”。Oracle數據庫一看,ZHS16GBK跟UTF8又不一樣,所以先查UTF8編碼表,找到編碼“3224678”對應的字符“中國”,再查ZHS16GBK編碼表,找到“中國”對應的編碼“167219”,然后就是把最后得到的這串編碼“167219”交給sqlplus程序。
最后,sqlplus直接把得到的這串編碼扔給客戶端操作系統,而操作系統只有ZHS16GBK編碼表,它不會問得到的這串編碼是什么格式的,只會直接到ZHS16GBK編碼表中去查“167219”對應的字符是什么,並把它交給應用程序顯示出來。這個顯示的結果是“中國”。
以上就是一個完整的從客戶端編碼並經過Oracle數據庫轉碼存入數據庫,然后從數據庫取出並轉碼交給客戶端顯示的實驗。
從以上過程我們可以得出以下一些結論:
1.對Oracle數據庫存取起作用的是這些:客戶端操作系統字符集、客戶端操作系統參數NLS_LANG、服務端數據庫字符集。
2.對Oracle數據庫不起作用的是服務端操作系統字符集。
3.客戶端操作系統只有一張編碼表,與客戶端字符集對應。
4.Oracle數據庫的字符集只有一個,並且固定,一般不改變。
5.存放在Oracle數據庫中的字符串的編碼格式只有一個,它就是數據庫的字符集所對應的編碼格式。
6.Oracle數據庫有很多張編碼表,可以在數據存入時將其它編碼格式的編碼轉換為數據庫字符集指定的格式,取出時從數據庫字符集指定的格式轉換為其它編碼格式。
7.整個架構中的轉碼只發生在Oracle數據庫邊界上,其它地方沒有。
8.Oracle是根據客戶端操作系統的參數NLS_LANG與自己的字符集進行對照來確定是否需要進行轉碼的。
最最重要的結論出來了:
9.Oracle數據庫如何選擇字符集?只有一個原則,那就是這個字符集要包含數據庫運行過程中所能存入的數據字符,通常作為中國人我們選擇ZHS16GBK,如果想再保險一點,選擇AL32UTF8。
10.服務器操作系統選擇什么字符集?這個字符集與數據庫字符集一點關系都沒有,只跟誰有關?操作系統管理員!所以它的選擇原則是,系統管理員想選擇什么就選擇什么。
11.客戶端操作系統選擇什么字符集?我是中國人,我用中文操作系統,所以我選擇ZHS16GBK。建議中國人都選擇ZHS16GBK。
12.客戶端操作系統參數NLS_LANG參數如何設置?這個只有一個設置方法,那就是與操作系統字符集相同。要不然會出問題的……
最最最最重要的一句話:
最好的,最不容易出字符集錯誤的就是:將數據庫字符集、客戶端字符集、客戶端操作系統NLS_LANG參數三個地方作同樣的設置。
另外再記錄一下EXP和IMP過程與字符集相關的事情。
EXP時,起作用的有Oracle數據庫的字符集和客戶端操作系統參數NLS_LANG兩項,這時服務器與客戶端操作系統字符集都不起作用。如果客戶端操作系統參數NLS_LANG與Oracle數據庫的字符集相同,那就直接導出,不需要轉碼,並且導出文件的字符集與上述兩項一樣;如果客戶端操作系統參數NLS_LANG與Oracle數據庫的字符集不同,那么導出時Oracle數據庫會將數據文件從Oracle數據庫的字符集編碼格式轉碼成客戶端操作系統參數NLS_LANG指定的編碼格式。總而言之一句話:導出文件的字符集格式與導出客戶端操作系統參數NLS_LANG一定相同。
IMP時,起作用的仍然是兩項,一項是DMP文件第二第三字節指定的字符集,另外一項是Oracle數據庫的字符集。兩個相同就不需要轉碼,兩個不同就轉成Oracle數據庫字符集指定的編碼格式。
最后記錄我遇到的幾個問題。
1.我前段時間測試過在CentOS上安裝Oracle11gR2,那時我設置過CentOS的字符集中“zh_CN.UTF-8”,並且安裝中文字體,當時也確實能得到我想要的結果,那就是:我安裝的Oracle數據庫的字符集是中文字符集ZHS16GBK。為什么呢,因為Oracle數據庫的字符集是默認地根據操作系統的字符集來的,並且我也就選擇它的默認字符集。所以沒有出錯。
但是,但是,現在我知道了,這個作為服務器的CentOS的字符集對Oracle數據庫沒有影響,所以現在讓我再來一回去選擇它是什么字符集,我會選擇en_US.UTF-8,甚至en_US.US7ASCII。為什么呢,因為在shell界面顯示中文確認是一個難題,所以管理CentOS,還是用英文吧,比較方便又對數據庫沒影響。隨它去吧。
2.英文操作系統安裝中文字符集oracle數據庫時,一定要注意在選擇數據庫字符集的時候慢一點,細心地選擇一個ZHS16GBK或者AL32UTF8。
3.DMP文件是US7ASCII字符集,要把它導入字符集是ZHS16GBK的數據庫中去,如何操作?第一步:安裝一個US7ASCII字符集的數據庫(比如說9i);第二步,將DMP文件導入該數據庫;第三步,設置導出客戶端操作系統參數NLS_LANG=ZHS16GBK,然后導出;第四步,將后導出的DMP文件導入字符集是ZHS16GBK的數據庫中去。理論上成功。需要做實驗測試。
4.曾經說到,一般情況下數據庫的字符集在數據庫安裝好之后就不可以更改。那么如果萬一領導說一定要改,怎么辦?比如說原來的字符集是ZHS16GBK,非要讓轉成UTF8,有沒有辦法?答案是有的,但是,但是不一定會全部成功,這里有一個嚴格超集的概念,這個概念在這篇文章里不講。答案是這么做,設置導出客戶端操作系統參數為UTF8,然后導出,這里,數據編碼格式會從ZHS16GBK轉碼成UTF8,然后再刪除ZHS16GBK的數據庫,新建一個UTF8的數據庫,再導入就可以了。