字符集

一、什麼是字符集?
實質就是按照一定的字符編碼方案,對一組特定的符號,分別賦予不同數值編碼的集合。Oracle數據庫最早支持的編碼方案是US7ASCII。
    Oracle的字符集命名遵循以下命名規則:

    即: <語言><比特位數><編碼>
    比如: ZHS16GBK表示採用GBK編碼格式、16位(兩個字節)簡體中文字符集
字符集是爲了方便將計算機語言與人類常用語言進行轉換的一種集合。字符集可以理解爲一張表,表中包含兩個列,分別是字符和對應的ASCII碼值。
字符集
    (1)用來存儲CHAR, VARCHAR2, CLOB, LONG等類型數據
    (2)用來標示諸如表名、列名以及PL/SQL變量等
    (3)用來存儲SQL和PL/SQL程序單元等
國家/地區字符集:
    (1)用以存儲NCHAR, NVARCHAR2, NCLOB等類型數據
    (2)國家字符集實質上是爲oracle選擇的附加字符集,主要作用是爲了增強oracle的字符處理能力,因爲NCHAR數據類型可以提供對亞洲使用定長多字節編碼的支持,而數據庫字符集則不能。國家字符集在oracle9i中進行了重新定義,只能在unicode編碼中的AF16UTF16和UTF8中選擇,默認值是AF16UTF16

二、字符集發揮的作用
在整個系統運行過程中,字符集在3個環節中發揮作用:
1.軟件在操作系統上運作時對用戶顯示的影響,此時顯示的字符采用操作系統定義的字符集進行顯示。我們在系統I/O編程的時候經常要指定字符集,C#中的Text.Encoding=Encoding.Default實際上就是告訴編譯器,文本使用系統定義的默認字符集進行編碼。sqlplus也是運行在操作系統上的軟件,當然要使用系統所指定的字符集對外顯示內容。
2.數據向oracle服務器端傳送前的通告。也就是sqlplus告訴服務器現在使用的字符集是什麼。(oracle用戶環境變量設置字符集的作用)
3.數據流到達服務器後,按照服務器所使用的字符集自動編譯客戶端的數據,然後存儲進系統。(OS端字符集的內容轉碼爲oracle自己使用的字符集後在轉換爲編碼存儲)

三、oracle、OS對應關係
1、操作系統可以和oracle服務器端字符集不相同,這樣只會影響如sqlplus等客戶端工具的顯示,但並不會妨礙數據庫的編碼存儲。
2、但是客戶端環境變量的字符集一定要和操作系統設置的字符集相匹配!否則會因爲錯誤的信息,造成oracle數據庫存入錯誤的編碼。這個錯誤在存入時不會有任何告警,並且幾乎是不可逆的錯誤。
這裏面涉及幾個詞彙的問題,子集、超集和嚴格超集
子集、超集:當一種字符集(字符集A)的編碼數值包含所有另一種字符集(字符集B)的編碼數值,並且兩種字符集相同編碼數值代表相同的字符時,則字符集A是字符集B的超級,或稱字符集B是字符集A的子集。(例如:UTF8就是AL32UTF8的子集)
嚴格超集:是指某個超集和它的子集,編碼方式嚴格對應。完全可以不需要轉碼就可以互通。

所以客戶端在環境變量中設置NLS_LANG時一定要與操作系統相同,並且需要填寫完整字符集名稱(包括語言、地域、字符集。例如:AMERICAN_AMERICA.AL32UTF8)

四、影響字符顯示的原因有多個方面
1)、os環境使用的字符集
2)、數據庫使用的字符集
3)、連接sqlplus工具的設置

一、OS操作系統字符集
字段插入數據庫時,oracle會通過詢問操作系統使用的字符集決定需不需要轉碼存儲,如果操作系統和數據庫使用相同的編碼方式,那麼在插入數據過程中就不需要進行轉碼操作,這樣能夠提高數據插入的速度。同樣數據讀取出來時,如果操作系統不支持當前字符集,這樣就會出現亂碼現象。
多數情況,操作系統在安裝過程中就決定使用的字符集,這樣能夠減少由於後期變更字符集造成的影響。

查看當前系統使用的字符集
echo $NLS_LANG
查看當前系統裝載的字符集
[oracle@dbdream ~]$ locale
LANG=zh_CN.UTF-8
LC_CTYPE="zh_CN.UTF-8"
LC_NUMERIC="zh_CN.UTF-8"
LC_TIME="zh_CN.UTF-8"
LC_COLLATE="zh_CN.UTF-8"
LC_MONETARY="zh_CN.UTF-8"
LC_MESSAGES="zh_CN.UTF-8"
LC_PAPER="zh_CN.UTF-8"
LC_NAME="zh_CN.UTF-8"
LC_ADDRESS="zh_CN.UTF-8"
LC_TELEPHONE="zh_CN.UTF-8"
LC_MEASUREMENT="zh_CN.UTF-8"
LC_IDENTIFICATION="zh_CN.UTF-8"
LC_ALL=
以上表示的LANG爲字符集名稱和數字、時間等等使用的字符集
locale -a 查看系統能夠支持的字符集(但不表示全部已經安裝)

可以通過三種方式進行字符集的修改
1、通過修改當前session只臨時改變當前會話的字符集
2、修改/etc/profile文件,改變再次創建的用戶使用的字符集(當用戶第一次登錄時,該文件被執行一次)
3、使用root用戶修改/etc/sysconfig/i18n,變更系統默認使用的字符集

1、設置當前session變量
# 常用unicode字符集
export LANG=american_america.AL32UTF8
# 常用中文字符集
export NLS_LANG="Simplified Chinese_china".ZHS16GBK
2、修改/etc/profile文件
增加export LANG=XXX字符集
設置生效
source /etc/profile
3、設置系統默認字符集
/etc/sysconfig/i18n可以永久修改系統使用的字符集
i18n是internationalization的縮寫,意思指i和n之間有18個字母。/etc/sysconfig/i18n裏面存放着系統的區域語言設置,可以使linux系統支持國際化信息顯示。就是支持多種字符集的轉換,避免出現亂碼。同一時間i18n只能是英文和一種選定的語言,例如英文+中文、英文+德文、英文+韓文等等。

[oracle@dbdream ~]$ vi /etc/sysconfig/i18n
LANG="zh_CN.UTF-8"
SUPPORTED="en_US.UTF-8"
SYSFONT="latarcyrheb-sun16"

LANG 表明你當前系統的語言環境變量設置 ,這裏是zh_CN.UTF-8
SUPPORTED 表明系統預置了那些語言支持 ,不在項目中的語言不能正常顯示
SYSFONT 定義控制檯終端字體,你文本登錄的時候顯示的字體是 latarcyrheb-sun16

二、數據庫字符集
1、數據庫服務器當前使用的字符集
select * from nls_database_parameters;或
select userenv('language') from dual;其來源於props$(系統表),表示當前數據庫使用的字符集。絕對不能通過update更新系統表,8i以後啓動數據庫字符集默認爲US7ASCII(缺省值)發現問題也只記錄alter日誌並不影響啓動。
    查詢結果中NLS_CHARACTERSET表示字符集,NLS_NCHAR_CHARACTERSET表示國家字符集
2、客戶端(實例)使用的字符集
select * from nls_instance_parameters;其來源於v$parameter,表示客戶端字符集的設置,可能是參數文件,環境變量或者是註冊表(Windows)


影響Oracle數據庫字符集最重要的參數是NLS_LANG參數:
它的格式如下: NLS_LANG = language_territory.charset
它有三個組成部分(語言、地域和字符集),每個成分控制了NLS子集的特性。
其中:
Language: 指定服務器消息的語言, 影響提示信息是中文還是英文
Territory: 指定服務器的日期和數字、貨幣格式等(這個在金融等環境中,同樣不能設置錯誤,否則會對貨幣符號等造成影響)
Charset:  指定字符集(絕對要與OS系統對應,oracle通過這個獲取輸入文字通過哪種字符集進行編碼的)
如:AMERICAN _ AMERICA. ZHS16GBK
從NLS_LANG的組成我們可以看出,真正影響數據庫字符集的其實是第三部分。
所以兩個數據庫之間的字符集只要第三部分一樣就可以相互導入導出數據,前面影響的只是提示信息是中文還是英文。

NLS參數查詢
    Oracle提供若干NLS參數定製數據庫和用戶機以適應本地格式,例如有NLS_LANGUAGE,NLS_DATE_FORMAT,NLS_CALENDER等,可以通過查詢以下數據字典或v$視圖查看。
NLS_DATABASE_PARAMETERS:顯示數據庫當前NLS參數取值,包括數據庫字符集取值
NLS_SESSION_PARAMETERS:  顯示由NLS_LANG 設置的參數,或經過alter session 改變後的參數值(不包括由NLS_LANG 設置的客戶端字符集)
NLS_INSTANCE_PARAMETE: 顯示由參數文件init.ora 定義的參數
V$NLS_PARAMETERS:顯示數據庫當前NLS參數取值

修改NLS參數
    使用下列方法可以修改NLS參數
    (1)修改實例啓動時使用的初始化參數文件
    (2)修改環境變量NLS_LANG
    (3)使用ALTER SESSION語句,在oracle會話中修改
    (4)使用某些SQL函數
    NLS作用優先級別:Sql function > alter session > 環境變量或註冊表> 參數文件> 數據庫默認參數

通過修改.bash_profile環境變量改變當前用戶的字符集
編輯 bash_profile文件進行永久設置
# vi .bash_profile
NLS_LANG="Simplified Chinese_china".ZHS16GBK
export NLS_LANG
# 使 bash_profile 設置生效
source .bash_profile
其實oracle讀取操作系統使用的字符集信息,就是從環境變量中讀取的。在這種情況下客戶端的環境變量如果錯誤,oracle就會將錯誤的編碼存入數據庫,造成輸入信息不正確的錯誤。並且還很難發現

Oracle服務器端字符集通常不建議修改
一個數據庫安裝時就需要考慮好字符集的選擇,變更字符集會對已經存儲的表造成字符編碼錯誤,當然如果切換的字符集與原有字符集是子集與超集關係,並且是嚴格超集。那麼多數情況不會產生編碼問題。如果需要修改字符集,通常需要導出數據庫數據,重建數據庫,再導入數據庫數據的方式來轉換,或通過ALTER DATABASE CHARACTER SET語句修改字符集,但創建數據庫後修改字符集是有限制的,只有新的字符集是當前字符集的超集時才能修改數據庫字符集,例如UTF8是US7ASCII的超集,修改數據庫字符集可使用ALTER DATABASE CHARACTER SET UTF8。

導入/導出與字符集轉換

EXP/IMP
由於使用exp/imp進行數據遷移時,數據從源數據庫到目標數據庫的過程中有四個環節涉及到字符集,如果這四個環節的字符集不一致,將會發生字符集轉換。
四個字符集是
(1)源數據庫字符集
(2)Export過程中用戶會話字符集(通過NLS_LANG設定)
(3)Import過程中用戶會話字符集(通過NLS_LANG設定)
(4)目標數據庫字符集

導入導出對應關係
EXP
     ____________ _________________ _____________
     |imp導入文件|<-|環境變量NLS_LANG|<-|數據庫字符集|
      ------------   -----------------   -------------
IMP
     ____________ _________________ _____________
     |imp導入文件|->|環境變量NLS_LANG|->|數據庫字符集|
      ------------   -----------------   -------------
導出的轉換過程
在Export過程中,如果源數據庫字符集與Export用戶會話字符集不一致,會發生字符集轉換,並在導出文件的頭部幾個字節中存儲Export用戶會話字符集的ID號。在這個轉換過程中可能發生數據的丟失。
例:如果源數據庫使用ZHS16GBK,而Export用戶會話字符集使用US7ASCII,由於ZHS16GBK是16位字符集,而US7ASCII是7位字符集,這個轉換過程中,中文字符在US7ASCII中不能夠找到對等的字符,所以所有中文字符都會丟失而變成“?? ”形式,這樣轉換後生成的Dmp文件已經發生了數據丟失,導入目標庫後也不能夠還原。
因此如果想正確導出源數據庫數據,則Export過程中用戶會話字符集應等於源數據庫字符集或是源數據庫字符集的超集

導入的轉換過程
(1)確定導出數據庫字符集環境
通過讀取導出文件頭,可以獲得導出文件的字符集設置
(2)確定導入session的字符集,即導入Session使用的NLS_LANG環境變量
IMP讀取導出文件
(1)讀取導出文件字符集ID,和導入進程的NLS_LANG進行比較
(2)如果導出文件字符集和導入Session字符集相同,那麼在這一步驟內就不需要轉換,如果不同,就需要把數據轉換爲導入Session使用的字符集。可以看出,導入數據到數據庫過程中發生兩次字符集轉換
第一次:導入文件字符集與導入Session使用的字符集之間的轉換,如果這個轉換過程不能正確完成,Import向目標數據庫的導入過程也就不能完成。
第二次:導入Session當前字符集與數據庫字符集之間的轉換,如果當前會話與數據庫字符集相同或者嚴格超集則不需要進行轉換,直接將編碼存入數據庫(與客戶端和服務器端插入數據相同)
oracle提供了字符集掃描工具(character set scanner)可以在導出前對需要導出數據進行測試,選擇最佳的解決方案

嘗試字符集AL32UTF8修改爲ZHS16GBK即從超集到子集
在SQL Puls中的命令如下:
SQL> conn /as sysdba
已連接。
1.關閉數據庫
SQL> shutdown immediate;
數據庫已關閉。
已經卸載數據庫。
2.啓動到Mount
SQL> startup mount
ORACLE例程已經啓動。
……   ………………
…………………………
數據庫裝載完畢。
SQL> ALTER SYSTEM ENABLE RESTRICTEDSESSION;
系統已更改。
SQL> ALTER SYSTEM SETJOB_QUEUE_PROCESSES=0;
系統已更改。
SQL> ALTER SYSTEM SETAQ_TM_PROCESSES=0;
系統已更改。
SQL> alter database open;
數據庫已更改。
SQL> ALTER DATABASE CHARACTER SET ZHS16GBK;
1行出現錯誤:
ORA-12712: new character set must be a superset of old characterset
提示我們的字符集:新字符集必須爲舊字符集的超集,這時我們可以跳過超集的檢查做更改:
SQL> ALTER DATABASE character set INTERNAL_USEZHS16GBK;
數據庫已更改。
--我們看到這個過程和之前ALTERDATABASE CHARACTER SET操作的內部過程是完全相同的,也就是說INTERNAL_USE提供的幫助就是使Oracle數據庫繞過了子集與超集的校驗。(這種方法不能保證數據完全可以轉碼,可能會出現亂碼)
因爲通過ALTER DATABASE CHARACTER SET語句修改字符集,但創建數據庫後修改字符集是有限制的,只有新的字符集是當前字符集的超集時才能修改數據庫字符集。
SQL> select * from v$nls_parameters;
RARAMETER
VALUE
NAS_LANGUAGE
SIMPLIFIED CHINESE
NLS_TERRITORY
CHINA
……
SQL> shutdown immediate;
SQL> startup
ORA-01081:???????ORACLE-???????意思是無法啓動已運行的ORACLE,請首先關閉它
至此,字符集的修改就完成了,我們可以通過輸入命令驗證一下,其結果已經變成了ZHS16GBK了,見下圖。
SQL> select userenv(‘language’) fromdual;

如何查詢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文件很大,比如有2G以上(這也是最常見的情況),用文本編輯器打開很慢或者完全打不開,可以用以下命令(在unix主機上):
cat exp.dmp |od -x|head -1|awk '{print $2 $3}'|cut -c 3-6
然後用上述SQL也可以得到它對應的字符集。
三、連接sqlplus工具的設置
這種問題,很大機率是發生在系統、數據庫都沒有錯誤,但讀取表內容時出現亂碼現象
例如CRT、Xmanager等設置時,需要在會話環境裏面設置語言或者編碼類型
例如:CRT設置

查詢表內容編碼使用的字符集
SQL> select tab,name,dump(name,1016) from scott.test;

TAB NAME
---------- ----------
DUMP(NAME,1016)
--------------------------------------------------------------------------------
30 大辰
Typ=1 Len=6 CharacterSet=AL32UTF8: e5,a4,a7,e8,be,b0
加入dump表示,將name列的編碼顯示出來。默認爲2進制可以輸入02、08、、10、16進行進制轉換顯示
進制前面加入的10表示顯示數據庫存儲數據時使用的字符集

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章