1. 程式人生 > >Oracle之 dmp匯入/匯出、資料庫操作等過程中的字符集問題

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的資料庫,再匯入就可以了。