原文出處:http://www.hkitn.com/article.php/5902
(註:部分文字稍稍修正及調整排版)
經常有同事咨詢 Oracle 資料庫字元集相關的問題,如在不同資料庫做數據
遷移、同其它系統交換數據等,常常因為字元集不同而導致遷移失敗或資料
庫內數據變成亂碼。現在我將 Oracle 字元集相關的一些知識做個簡單總結,
希望對大家今後的工作有所幫助。
本文從定義入手來講解對 Oracle 資料庫字元集如何全面認識。
一、什麼是 Oracle 字元集
Oracle 字元集是一個位元組數據的解釋的符號集合,有大小之分,有相互
的包容關係。Oracle 支援國家語言的體系結構允許你使用本地化語言來
存儲,處理,檢索數據。它使資料庫工具,錯誤消息,排序次序,日期,
時間,貨幣,數字,和日曆自動適應本地化語言和平台。
影響Oracle資料庫字元集最重要的參數是 NLS_LANG 參數。
它的格式如下:
NLS_LANG = language_territory.charset
它有三個組成部分(語言、地域和字元集),每個成分控制了 NLS 子集的
特性,其中:
Language 指定伺服器消息的語言,
territory 指定伺服器的日期和數字格式,
charset 指定字元集。
例如: AMERICAN_AMERICA.ZHS16GBK
從 NLS_LANG 的組成我們可以看出,真正影響資料庫字元集的其實是第三
部分。所以兩個資料庫之間的字元集只要第三部分一樣就可以相互導入導
出數據,前面影響的只是提示資訊是中文還是英文。
二、如何查詢 Oracle 的字元集
很多人都碰到過因為字元集不同而使數據導入失敗的情況。
這涉及三方面的字元集:
1. 是 Oracle Server端的字元集。
2. 是 Oracle Client端的字元集。
3. 是 dmp 文件的字元集。
在做數據導入的時候,需要這三個字元集都一致才能正確導入。
1、查詢 Oracle Server端的字元集
有很多種方法可以查出 Oracle Server 端的字元集,比較直觀的查詢
方法是以下這種:
SQL> select userenv('language') from dual;
結果類似如下:
AMERICAN_AMERICA. ZHS16GBK
2、如何查詢 dmp 文件的字元集
用 Oracle 的 exp 工具導出的 dmp 文件也包含了字元集資訊, dmp
文件的第 2 和第 3 個位元組記錄了 dmp 文件的字元集。如果 dmp
文件不大,比如只有幾 M 或幾十 M,可以用 UltraEdit 打開 (16進制
方式),看第 2 和第 3 個位元組的內容,如 0354,然後用以下 SQL
查出它對應的字元集:
SQL> select nls_charset_name(to_number('0354','xxxx')) from dual;
結果類似如下:
ZHS16GBK
如果 dmp 文件很大,比如有 2 G 以上 (這也是最常見的情況),用文本
編輯器打開很慢或者完全打不開,可以用以下命令 (在 unix 主機上):
cat exp.dmp |od -x|head -1|awk '{print $2 $3}'|cut -c 3-6
然後用上述 SQL 也可以得到它對應的字元集。
3、查詢 Oracle Client端的字元集
這個比較簡單。在 Windows 平台下,就是註冊表裏面相應 OracleHome
的 NLS_LANG。還可以在 Dos 窗口裏面自己設定,比如:
set nls_lang=AMERICAN_AMERICA.ZHS16GBK
這樣就只影響這個窗口裏面的環境變量。
在 unix 平台下,就是環境變量 NLS_LANG。
$echo $NLS_LANG
AMERICAN_AMERICA.ZHS16GBK
如果檢查的結果發現 Server 端與 Client 端字元集不一致,請統一
修改為與 Server 端相同的字元集。
三、修改 Oracle 的字元集
上文說過,Oracle 的字元集有互相的包容關係。
如 US7ASCII 就是 ZHS16GBK 的子集,從 US7ASCII 到 ZHS16GBK 不會有
數據解釋上的問題,不會有數據丟失。
在所有的字元集中 utf8 應該是最大,因為它基於 unicode,雙位元組保存
字元 (也因此在存儲空間上佔用更多)。
一旦資料庫創建後,資料庫的字元集理論上講是不能改變的。因此,在設計
和裝設之初考慮使用哪一種字元集十分重要。
根據 Oracle 的官方說明,字元集的轉換是從子集到超集受支援,反之不行。
如果兩種字元集之間根本沒有子集和超集的關係,那麼字元集的轉換是不受
Oracle 支援的。
對資料庫 Server 而言,錯誤的修改字元集將會導致很多不可測的後果,可能
會嚴重影響資料庫的正常運行,所以在修改之前一定要確認兩種字元集是否存
在子集和超集的關係。一般來說,除非萬不得已,我們不建議修改 Oracle 資
料庫 Server 端的字元集。
特別說明,我們最常用的兩種字元集 ZHS16GBK 和 ZHS16CGB231280 之間不
存在子集和超集關係,因此理論上講這兩種字元集之間的相互轉換不受支援。
1、修改 Server 端字元集(不建議使用)
在 Oracle 8之前,可以用直接修改數據字典表 props$ 來改變資料庫的
字元集。但 Oracle 8 之後,至少有三張系統表記錄了資料庫字元集的
資訊,只改 props$ 表並不完全,可能引起嚴重的後果。正確的修改方
法如下:
$sqlplus /nolog
SQL> conn / as sysdba;
若此時資料庫伺服器已啟動,則先執行 SHUTDOWN IMMEDIATE 命令關閉
資料庫伺服器,然後執行以下命令:
SQL> STARTUP MOUNT;
SQL> ALTER SYSTEM ENABLE RESTRICTED SESSION;
SQL> ALTER SYSTEM SET JOB_QUEUE_PROCESSES=0;
SQL> ALTER SYSTEM SET AQ_TM_PROCESSES=0;
SQL> ALTER DATABASE OPEN;
SQL> ALTER DATABASE CHARACTER SET ZHS16GBK;
SQL> ALTER DATABASE national CHARACTER SET ZHS16GBK;
SQL> SHUTDOWN IMMEDIATE;
SQL> STARTUP
2、修改 dmp 文件字元集
上文說過,dmp 文件的第 2 第 3 位元組記錄了字元集資訊,因此直接
修改 dmp 文件的第 2 和第 3 位元組的內容就可以「騙」過 Oracle 的
檢查。
這樣做理論上也僅是從子集到超集可以修改,但很多情況下在沒有子集
和超集關係的情況下也可以修改,我們常用的一些字元集,如:
US7ASCII、WE8ISO8859P1、ZHS16CGB231280、ZHS16GBK
基本都可以改。因為改的只是 dmp 文件,所以影響不大。
具體的修改方法比較多,最簡單的就是直接用 UltraEdit 修改 dmp 文
件的第 2 和第 3 個位元組。
比如想將 dmp 文件的字元集改為 ZHS16GBK,可以用以下 SQL 查出該種
字元集對應的 16 進制代碼:
SQL> select to_char(nls_charset_id('ZHS16GBK'), 'xxxx') from dual;
0354
然後將 dmp 文件的 2、3 位元組修改為 0354 即可。
如果 dmp 文件很大,用 UltraEdit 無法打開,就需要用程式的方法了。
網上有人用 java 存儲過程寫了轉換的程式 (用 java 存儲過程的好處
是通用性教好,缺點是比較麻煩)。我在 Windows 下測試過。但要求
Oracle 資料庫一定要裝設 JVM 選項。有興趣的朋友可以研究一下程式
代碼。
===