這幾天在工做中碰到一個字符亂碼的問題,發如今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.
CTBLAZER@106orcl>commit ;
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
CTBLAZER@106orcl>
使用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
CTBLAZER@106orcl>
一樣建一個文本文件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 中國
CTBLAZER@106orcl>
因 此,在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.
CTBLAZER@106orcl>commit;
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
CTBLAZER@106orcl>
結果頗有意思,數據庫中存儲的再也不是像實驗一中的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 涓浗
CTBLAZER@106orcl>
若是咱們把cmd的codepage設置爲UTF8格式的,咱們就能夠看到是正常的顯示了(這時仍是要求客戶端與服務器端的字符集一致)
Active code page: 65001
CTBLAZER@106orcl>select * from c_language where id = 'zh-CN';
ID LANGUAGENAME
-------------------- ------------------------------
zh-CN 中國
CTBLAZER@106orcl>
注意,這個實驗的結果比較巧合,若是將sqlplus客戶端的字符集不是設置ZHS16GBK,而是設爲其餘的字符集,那麼Oracle中存儲的就不必定是UTF8的編碼了。
實際上,若是客戶端和服務端的字符集不一致,Oracle底層會對字符編碼進行轉換,具體如何轉換我也還不清楚,望有高手深刻研究。
總的來講,在實際中若是碰到亂碼狀況,須要知曉Oracle客戶端字符集,服務器端字符集以及操做系統字符集這幾個之間的關係。1. 若是客戶端和服務器端的字符集一致,Oracle不會進行編碼轉換。直接按存儲操做系統相關的編碼格式存儲。在查詢的時候,也不會轉換,而是直接返回數據庫中存儲的編碼2. 若是客戶端和服務器端的字符集不一致,Oracle會在底層對存儲的字符進行編碼轉換。在查詢返回的時候,一樣會轉換一次。