天天看點

剪不斷,理還亂--Oracle的字元集亂碼問題 剪不斷,理還亂--Oracle的字元集亂碼問題

剪不斷,理還亂--Oracle的字元集亂碼問題

  • 作者: 三十而立
  • 時間:2009年11月03日 12:20:54
  • 請尊重原創作品。轉載請保持文章完整性,并以超連結形式注明原始作者“inthirties(三十而立) ”和出處”http://blog.csdn.net/inthirties/archive/2009/11/03/4759844.aspx ”,深入讨論可以聯系[email protected] 。

 咋又亂碼了,Oracle的管理者經常會碰到字元亂碼的問題,明明看到很的文檔裡都提到過隻要用戶端和伺服器端的字元集一緻。就能解決亂碼問題。

可是我這裡明明伺服器和用戶端字元集一緻卻顯示的時候是亂碼,用戶端和伺服器字元集不一緻,反而到不顯示亂碼了。真是越整越糊塗,剪不斷,理還亂。 這不,昨天CSDN上的,一個網友在有關字元集的實驗中,就碰到這個問題叻,怎麼想都想不通,陷入了上面的疑惑之中,隻得在CSDN上求助。 我們先一起看看他的問題和相關的實驗吧。 實驗主要是針對字元集為研究目的來做的。 Oracle資料庫伺服器的字元集 SQL  >   select   *   from   nls_database_parameters; PARAMETER                      VALUE ------------------------------ ------------------------------ NLS_LANGUAGE                   AMERICAN NLS_TERRITORY                  AMERICA NLS_CURRENCY                   $ NLS_ISO_CURRENCY               AMERICA NLS_NUMERIC_CHARACTERS         ., NLS_CHARACTERSET               UTF8 ..................... 用一個表來做中文插入和查詢的實驗 SQL  >   create   table   tt(name   varchar2  (  100   )); 目前系統的字元集 C:Documents   and   SettingsAdmin  >  chcp  活動的代碼頁:   936 這裡chcp查詢的是window系統的cmd的字元集編碼,目前的cmd的編碼是GBK 下面就就設定不同的用戶端的字元集來進行插入中文的測試叻。 先設定用戶端的字元集編碼和伺服器的一緻 C:Documents   and   SettingsAdmin  >  set   nls_lang  =  AMERICAN_AMERICA.UTF8 下面插入資料 (實驗一) SQL>  insert    into    tt    values   (   '   一   '   ); 1 row created. SQL>  insert    into    tt    values   (   '   二   '   ); 1 row created. SQL> commit; SQL  >   select   *   from   tt; NAME --  -- 一  二  

這裡一切都還正确,伺服器和用戶端的字元集一緻,這裡顯示沒有亂碼。 接下來的實驗,就開始讓人琢磨不透了 (實驗二) 現在切換用戶端的字元集到GBK的字元集。 C:Documents   and   SettingsAdmin  >  set   nls_lang  =  SIMPLIFIED CHINESE_CHINA.ZHS16GBK 先看看上面的記錄對不對 SQL  >   select   *   from   tt; NAME  --  ------------------------------------------------------------------------------    ?  ??  

亂碼出現,這時候,我們可能對結果還比較肯定,還可以很理直氣壯的,伺服器和用戶端的字元集不一緻,當然就亂碼了呀。那麼看看下面的吧。 SQL  >   insert   into   tt   values  (  '  三  '  );  已建立   1    行。  SQL  >   insert   into   tt   values  (  '  四  '  );  已建立   1    行。  

SQL  >   commit  ;  

SQL  >   select   *   from   tt;  

NAME  --  ------------------------------------------------------------------------------    ?  ??  三  四  

奇怪了吧,上面還理直氣壯的說過,由于伺服器和用戶端字元集不一緻,應該是亂碼的,怎麼這後面兩個,卻不是亂碼了。顯示的居然還是正确的叻。 呵呵呵,是不是亂了呀。 先不忙着給你解釋,讓你先繼續下面的實驗, 用戶端再次切換到UTF8的字元集上

(實驗三)

C:/Documents  and  Settings/Admin > set  nls_lang = AMERICAN_AMERICA.UTF8 再來看看上面的結果 SQL >  select  *  from  tt;  NAME  -- ------------------------------------------------------------------------------   一  二  涓  鍥

這三和四又不知道變成什麼樣子了,反正是沒有正确顯示出來,究竟是怎麼回事呀,這用戶端和伺服器端是一樣的字元集,按道理,應該不亂碼的,咋這裡又亂碼了,上面三,四在不一樣的字元集倒沒有亂碼,倒是把人整迷糊叻。究竟是怎麼回事呢? 下面我們來揭開這個謎團吧。 先用dump函數看看我們的存的字元的真面目吧。 SQL >  select  name, dump (name)  from  tt; NAME  DUMP (NAME)  -- ---------------------------- ------------------------------   一 Typ = 1  Len = 2 :  210 , 187   二 Typ = 1  Len = 2 :  182 , 254   涓 Typ = 1  Len = 3 :  228 , 184 , 137   鍥 Typ = 1  Len = 3 :  229 , 155 , 155 dump揭露了我們存儲的真面目,dump不做詳細介紹了。有興趣的可以去查查文檔 這裡可以看都是一個中文,一,二存進去隻有兩個位元組,而三,四存進去是3個位元組,究竟是怎麼回事叻。導緻這裡的差異的就是字元集的原因。 還記得,一,二是用UTF8的用戶端存進去的吧。對于Oracle來說,Oracle認識的用戶端字元集,系統的字元集他是不關心 的,當伺服器字元集和用戶端字元集一緻的時候,Oracle在用戶端和伺服器端交換的時候,不會做字元集的轉換,這樣,我們輸入的中文編碼兩個位元組,原封 不動的交給伺服器端,伺服器存到資料庫裡,也就是2個位元組叻。後面的三,四的時候,用戶端字元集是GBK,和伺服器不一緻,是以在服務端和用戶端互動中, 發生轉碼,這裡用戶端的中文編碼轉換到UTF8,是三個位元組,這樣三,四存到資料庫裡都是三個位元組叻。 既然是這樣,那麼用UTF8看到的一,二是正常的,他們存的是GBK編碼的位元組,而三,四存的是UTF8,和你的用戶端一緻的字元集,反倒是不 正常的叻。問題還是用上面的的原因來解釋,根據dump的資訊,我們可以看到,一二,是以GBK存儲的,三,四是用UTF8編碼存的。當我們select 的時候,資料庫找到了一二三四的資料,傳回給Oracle用戶端,當UTF8時,和資料庫字元集一緻,這個過程不發生轉碼,是以這裡的位元組原封不動的傳到 了用戶端顯示,一,二是GBK編碼的2個位元組,顯示的時候,我們的用戶端的系統的cmd是GBK的字元集,也就正常顯示了。而三,四叻。傳過來的是 UTF8編碼的位元組,在GBK的cmd環境裡,當然就不能正常顯示了喲,也就出現了怪異的字元了。 當我們的用戶端的字元集為GBK的時候呢。和伺服器端的不一緻,那麼這個過程中伺服器到用戶端的過程中會進行轉碼,有UTF8轉至到GBK, 而一,二已經是GBK的2個位元組了,這個過程肯定不對,是以出現亂碼,這也就是實驗三的結果。而三,四呢,本身存的就是UTF8編碼的三個位元組,其實就是 有GBK剛才轉成的,現在回過去,當然是可以轉變為GBK的編碼了。然後cmd是GBK的編碼,當然就正常的顯示了。 也就出現了實驗二的結果。  通過這裡的實驗,我們的分析的依據就是伺服器段和Oracle用戶端的字元集的一緻的關系,如果不一緻,在伺服器和Oracle用戶端互動的過 程中,将會按兩種字元集編碼進行轉碼,反之,如果一緻的話,在互動的過程中,不進行編碼,字元直接的進行傳輸。 這就是這裡最基本的分析原理了。 安裝我們的分析原理,我們一起來猜想一下,我現在是如果用戶端換在linux的utf8的作業系統環境中, 現在我們的Oracle的用戶端環境的字元集是utf8 [oracle$ oracle]export  nls_lang = AMERICAN_AMERICA.UTF8 在這樣的sqlplus環境中,我們還是通路上的表 SQL >  select  name, dump (name)  from  tt;

這時候的結果,将如何顯示叻?這裡的一,二,三,四四條記錄,是和我們的實驗三(Oracle的用戶端也是UTF8的編碼)一樣的結果嘛?

如果我們在這個Linux的sqlplus的環境中,再次插入五,六兩條記錄,他們又是怎樣的,這個新插入的,五,六兩條記錄在實驗二和實驗三的環境裡進行查詢時,又會出現什麼樣的情況叻?

最後留下的這兩個問題,就是對大家的檢驗,如果大家可以分析出結果,并能夠嘗試一下的話,那麼大家就不再怕這個剪不斷,理還亂的Oracle資料庫的字元亂碼的問題叻。

有興趣,并且懂java的朋友,可以用java程式再來通路一下這裡的所有資料,看又是什麼樣的情況?提示,java的thin的連接配接也是Oracle的用戶端,但是不像oci以及其他用戶端的程式那樣,java不需要指定用戶端的字元集。

做實驗的過程中有問題的朋友,可以發郵件把你的問題回報給我,我們一起讨論。