Oracle字符集轉換

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.

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會在底層對存儲的字符進行編碼轉換。在查詢返回的時候,同樣會轉換一次。

 

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