Oracle之 dmp匯入/匯出、資料庫操作等過程中的字元集問題
開篇:因為要定位一個 關於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的資料庫,再匯入就可以了。