http://xiangqinghu1988.blog.163.com/blog/static/58822991201222231517193/
http://www.orafaq.com/wiki/NLS_LANG
這幾天在工作中碰到一個字符亂碼的問題,發現在cmd窗口的sqlplus中直接update一箇中文和使用@調用一個文件作同樣更新的時候,存儲的結果竟不一樣。一時比較迷惑,對Oracle如何處理各個字符集的問題不是很清楚。特此通過一些資料和實驗總結,系統學習一下Oracle中字符集的相關知識。
一. 字符集的基礎知識:
在網絡上已有不少網友對字符集進行了研究,個人覺得有幾個不錯的網站可以參考
http://blog.csdn.net/tianlesoftware/article/details/4915223
http://www.itstreets.com/post/34.html
http://www.oraclefans.cn/forum/showblog.jsp?rootid=3303
二. Oracle字符集的轉換
以下用幾個實驗來學習一下Oracle對各種字符集的處理
此處涉及3個概念:數據庫服務端的字符集,客戶端的字符集和操作系統的字符集
實驗環境中:數據庫服務端的字符集是AL32UTF8
1. 客戶端和服務器端的字符集一致的時候
實驗一:客戶端字符集也是AL32UTF8, 操作系統的字符集是GB2312
CTBLAZER@106orcl>update c_language set languagename='中國' where id = 'zh-CN';
1 row updated.
Commit complete.
CTBLAZER@106orcl>select dump(languagename, 1016) from c_language where id = 'zh-CN';
DUMP(LANGUAGENAME,1016)
-----------------------------------------------------------------------------------------
Typ=1 Len=4 CharacterSet=AL32UTF8: d6,d0,b9,fa
使用dump函數以16進制查看在數據庫中的存儲,可以看出“中國”這2個字,在數據庫中的存儲編碼是:d6,d0,b9,fa
先建一個文件t1.txt,寫上“中國”這2個字,以GB2312(codepage 爲936)的格式保存。
在cmd窗口使用type命令查看,可以看到此時在cmd窗口能正確顯示文本。
V:\>chcp
Active code page: 936
V:\>type e:\t1.txt
中國
V:\>
再用winhex工具查看t1.txt文本,可以看到其16進制編碼爲:D6,D0,B9,FA
實驗二:在sqlplus環境中使用@來調用一個sql文件,文件的內容是和實驗一同樣的一句sql,文件格式爲UTF8
服務器和客戶端的字符集依然是AL32UTF8, 操作系統的codepage爲936
CTBLAZER@106orcl>select dump(languagename, 1016) from c_language where id = 'zh-CN';
DUMP(LANGUAGENAME,1016)
---------------------------------------------------------------------------------------
Typ=1 Len=6 CharacterSet=AL32UTF8: e4,b8,ad,e5,9b,bd
同樣建一個文本文件t2.txt,以UTF8的格式保存。再使用WinHex查看, 可以看到其16進制編碼爲:E4, B8, AD, E5, 9B, BD
通過這2個實驗,可以清楚的看出,在客戶端和數據庫服務端的字符集一致的時候,Oracle並不進行存儲轉換。如在cmd窗口之中,
以操作系統默認的GB2312編碼的“中國”,和以文件方式按UFT8編碼格式的“中國”,都會直接存儲到Oracle服務器上。
但是我們會發現,按UTF8格式編碼的存儲,在cmd裏面查詢的結果會是亂碼。如下:
CTBLAZER@106orcl>host chcp
Active code page: 936
CTBLAZER@106orcl>col languagename format A30
CTBLAZER@106orcl>select * from c_language where id='zh-CN';
ID LANGUAGENAME
-------------------- ------------------------------
zh-CN 涓浗
CTBLAZER@106orcl>
這是因爲我機器cmd窗口的codepage 默認是936,而客戶端和服務器端的字符集都一樣,Oracle會不做任何轉換的把保存的字符編碼
直接返回回來,但是數據庫裏面的編碼是按UTF8的存儲的,而cmd窗口的字符編碼爲GB2312,故而是顯示的亂碼。
我們可以將cmd窗口的codepage先改爲UTF8(codepage爲65001)格式的,則可以看到正常的顯示“中國”這2個字了。如下:
(如何修改codepage,可以參考:http://xiangqinghu1988.blog.163.com/blog/static/58822991201222232456746/)
Active code page: 65001
CTBLAZER@106orcl>select * from c_language where id = 'zh-CN';
ID LANGUAGENAME
-------------------- ------------------------------
zh-CN 中國
因此,在Oracle沒有施行字符轉換的時候,即(客戶端和服務器端的字符集是一致的時候),如果出現亂碼,那麼則是表明客戶端的環境編碼(如cmd窗口的編碼)和Oracle數據庫存儲的字符編碼(cmd窗口直接用sqlplus更新和調用一個UTF8文件格式的更新)不一致。如果要查看,需要修改客戶端的環境編碼。比如修改cmd的codepage。
2. 客戶端和服務器端的字符集不一致的時候
實驗三:將客戶端的字符集設置爲ZHS16GBK,在sqlplus裏面直接更新記錄如下:
V:\>set NLS_LANG=American_America.ZHS16GBK
V:\>sqlplus ctblazer@106orcl
SQL*Plus: Release 11.2.0.1.0 Production on Thu Mar 22 15:29:43 2012
Copyright (c) 1982, 2010, Oracle. All rights reserved.
Enter password:
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
CTBLAZER@106orcl>Update c_language set languagename='中國' where id = 'zh-CN';
1 row updated.
Commit complete.
CTBLAZER@106orcl>col languagename format A30
CTBLAZER@106orcl>select * from c_language where id = 'zh-CN';
ID LANGUAGENAME
---------------------------------------- ------------------------------
zh-CN 中國
CTBLAZER@106orcl>select dump(languagename, 1016) from c_language where id = 'zh-CN';
DUMP(LANGUAGENAME,1016)
-----------------------------------------------------------------------------------------
Typ=1 Len=6 CharacterSet=AL32UTF8: e4,b8,ad,e5,9b,bd
結果很有意思,數據庫中存儲的不再是像實驗一中的d6,d0,b9,fa, 而是很巧的變成了UTF8格式的存儲了。如果我們開另外一個
sqlplus窗口,其客戶端字符集爲AL32UTF8,這時來查看一下這條記錄如下,發現亂碼了
V:\>sqlplus ctblazer@106orcl
SQL*Plus: Release 11.2.0.1.0 Production on Thu Mar 22 15:36:56 2012
Copyright (c) 1982, 2010, Oracle. All rights reserved.
Enter password:
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
CTBLAZER@106orcl>col languagename format A30
CTBLAZER@106orcl>select * from c_language where id = 'zh-CN';
ID LANGUAGENAME
-------------------- ------------------------------
zh-CN 涓浗
如果我們把cmd的codepage設置爲UTF8格式的,我們就可以看到是正常的顯示了(這時還是要求客戶端與服務器端的字符集一致)
Active code page: 65001
CTBLAZER@106orcl>select * from c_language where id = 'zh-CN';
ID LANGUAGENAME
-------------------- ------------------------------
zh-CN 中國
注意,這個實驗的結果比較巧合,如果將sqlplus客戶端的字符集不是設置ZHS16GBK,而是設爲其他的字符集,那麼Oracle中存儲的就不一定是UTF8的編碼了。
實際上,如果客戶端和服務端的字符集不一致,Oracle底層會對字符編碼進行轉換,具體如何轉換我也還不清楚,望有高手深入研究。
總的來說,在實際中如果碰到亂碼情況,需要知曉Oracle客戶端字符集,服務器端字符集以及操作系統字符集這幾個之間的關係。
1. 如果客戶端和服務器端的字符集一致,Oracle不會進行編碼轉換。直接按存儲操作系統相關的編碼格式存儲。在查詢的時候,也不會轉換,而是直接返回數據庫中存儲的編碼
2. 如果客戶端和服務器端的字符集不一致,Oracle會在底層對存儲的字符進行編碼轉換。在查詢返回的時候,同樣會轉換一次。