字符集問題的初步探討(三)
2024-07-21 02:07:45
供稿:網(wǎng)友
link:
http://www.eygle.com/special/nls_character_set_03.htm
2. 字符集的更改
數(shù)據(jù)庫創(chuàng)建以后,如果需要修改字符集,通常需要重建數(shù)據(jù)庫,通過導(dǎo)入導(dǎo)出的方式來轉(zhuǎn)換。
我們也可以通過以下方式更改
alter database character set
注意:修改數(shù)據(jù)庫字符集時(shí)必須謹(jǐn)慎,修改之前一定要為數(shù)據(jù)庫備份。由于不能回退這項(xiàng)操作,因此可能會(huì)造成數(shù)據(jù)丟失或者損壞。
這是最簡單的轉(zhuǎn)換字符集的方式,但并不總是有效。
這個(gè)命令在oracle8時(shí)被引入oracle,這個(gè)操作在本質(zhì)上并不轉(zhuǎn)換任何數(shù)據(jù)庫字符,只是簡單的更新數(shù)據(jù)庫中所有跟字符集相關(guān)的信息。
這意味著,你只能在新字符集是舊字符集嚴(yán)格超集的情況下使用這種方式轉(zhuǎn)換。
所謂超集是指:
當(dāng)前字符集中的每一個(gè)字符在新字符集中都可以表示,并使用同樣的代碼點(diǎn)
比如很多字符集都是us7ascii的嚴(yán)格超集。
如果不是超集,將獲得以下錯(cuò)誤:
sql> alter database character set zhs16cgb231280;
alter database character set zhs16cgb231280
*
error at line 1:
ora-12712: new character set must be a superset of old character set
下面我們來看一個(gè)測(cè)試(以下測(cè)試在oracle9.2.0下進(jìn)行,oracle9i較oracle8i在編碼方面有較大改變,在oracle8i中,測(cè)試結(jié)果可能略有不同):
sql> select name,value$ from props$ where name like '%nls%';name value$------------------------------ ------------------------------nls_language americannls_territory americanls_currency $nls_iso_currency americanls_numeric_characters .,nls_characterset us7asciinls_calendar gregoriannls_date_format dd-mon-rrnls_date_language american……………….nls_nchar_characterset al16utf16nls_rdbms_version 9.2.0.4.020 rows selected.sql> select name,dump(name) from eygle.test;name dump(name)------------------------------------------------------測(cè)試 typ=1 len=4: 178,226,202,212test typ=1 len=4: 116,101,115,1162 rows selected.
轉(zhuǎn)換字符集,數(shù)據(jù)庫應(yīng)該在restricted模式下進(jìn)行.
c:/>sqlplus "/ as sysdba"sql*plus: release 9.2.0.4.0 - production on sat nov 1 10:52:30 2003copyright (c) 1982, 2002, oracle corporation. all rights reserved.connected to:oracle9i enterprise edition release 9.2.0.4.0 - productionwith the partitioning, oracle label security, olap and oracle data mining optionsjserver release 9.2.0.4.0 - productionsql> shutdown immediatedatabase closed.database dismounted.oracle instance shut down.sql> startup mount;oracle instance started.total system global area 76619308 bytesfixed size 454188 bytesvariable size 58720256 bytesdatabase buffers 16777216 bytesredo buffers 667648 bytesdatabase mounted.sql> alter session set sql_trace=true;session altered.sql> alter system enable restricted session;system altered.sql> alter system set job_queue_processes=0;system altered.sql> alter system set aq_tm_processes=0;system altered.sql> alter database open;database altered.sql> set linesize 120sql> alter database character set zhs16gbk;alter database character set zhs16gbk*error at line 1:ora-12721: operation cannot execute when other sessions are activesql> alter database character set zhs16gbk;alter database character set zhs16gbk*error at line 1:ora-12716: cannot alter database character set when clob data exists在oracle9i中,如果數(shù)據(jù)庫存在clob類型字段,那么就不允許對(duì)字符集進(jìn)行轉(zhuǎn)換sql>
這時(shí)候,我們可以去查看alert<sid>.log日志文件,看clob字段存在于哪些表上:
alter database character set zhs16gbk
sys.metastylesheet (stylesheet) - clob populated
ora-12716 signalled during: alter database character set zhs16gbk...
對(duì)于不同情況,oracle提供不同的解決方案,如果是用戶數(shù)據(jù)表,一般我們可以把包含clob字段的表導(dǎo)出,然后drop掉相關(guān)對(duì)象,
轉(zhuǎn)換后再導(dǎo)入數(shù)據(jù)庫;對(duì)于系統(tǒng)表,可以按照以下方式處理:
sql> truncate table metastylesheet;
table truncated.
然后可以繼續(xù)進(jìn)行轉(zhuǎn)換!
sql> alter session set sql_trace=true;session altered.sql> alter database character set zhs16gbk;database altered.sql> alter session set sql_trace=false;session altered.
在9.2.0中,轉(zhuǎn)換完成以后,可以通過運(yùn)行catmet.sql腳本來重建metastylesheet表:
sql> @?/rdbms/admin/catmet.sql
轉(zhuǎn)換后的數(shù)據(jù):
sql> select name,value$ from props$ where name like '%nls%';name value$------------------------------ ------------------------------nls_language americannls_territory americanls_currency $nls_iso_currency americanls_numeric_characters .,nls_characterset zhs16gbk…..nls_nchar_characterset al16utf16nls_rdbms_version 9.2.0.4.020 rows selected.sql> select * from eygle.test;name------------------------------測(cè)試test2 rows selected.
提示:
通過設(shè)置sql_trace,我們可以跟蹤很多數(shù)據(jù)庫的后臺(tái)操作,這個(gè)工具是dba常用的“利器”之一。
我們簡單看一下數(shù)據(jù)庫更改字符集時(shí)的后臺(tái)處理,我提取了主要的更新部分。
通過以下跟蹤過程,我們看到數(shù)據(jù)庫在更改字符集的時(shí)候,主要更新了12張數(shù)據(jù)字典表,修改了數(shù)據(jù)庫的原數(shù)據(jù),這也證實(shí)了我們以前的說法:
這個(gè)更改字符集的操作在本質(zhì)上并不轉(zhuǎn)換任何數(shù)據(jù)庫字符,只是簡單的更新數(shù)據(jù)庫中所有跟字符集相關(guān)的信息。
update col$ set charsetid = :1 where charsetform = :2update argument$ set charsetid = :1 where charsetform = :2update collection$ set charsetid = :1 where charsetform = :2update attribute$ set charsetid = :1 where charsetform = :2update parameter$ set charsetid = :1 where charsetform = :2update result$ set charsetid = :1 where charsetform = :2update partcol$ set spare1 = :1 where charsetform = :2update subpartcol$ set spare1 = :1 where charsetform = :2update props$ set value$ = :1 where name = :2update "sys"."kotad$" set sys_nc_rowinfo$ = :1 where sys_nc_oid$ = :2update seq$ set increment$=:2,minvalue=:3,maxvalue=:4,cycle#=:5,order$=:6, cache=:7,highwater=:8,audit$=:9,flags=:10 where obj#=:1update kopm$ set metadata = :1, length = :2 where name='db_fdo'
在這里我們順便糾正一個(gè)由來以及的錯(cuò)誤方法.
經(jīng)常可以在網(wǎng)上看到這樣的更改字符集的方法:
1)用sys用戶名登陸oracle。
2)查看字符集內(nèi)容
sql>select * from props$;
3)修改字符集
sql> update props$ set value$='新字符集' where name='nls_characterset'
4) commit;
我們看到很多人在這個(gè)問題上遇到了慘痛的教訓(xùn),使用這種方式更改字符集,如果你的value$值輸入了不正確的字符集,在8i中那么你
的數(shù)據(jù)庫可能會(huì)無法啟動(dòng),這種情況是非常嚴(yán)重的,有時(shí)候你必須從備份中進(jìn)行恢復(fù);如果是在9i中,可以重新啟動(dòng)數(shù)據(jù)庫后再修改回正
確的字符集。但是我們?nèi)匀徊唤ㄗh使用這種方式進(jìn)行任何數(shù)據(jù)庫修改,這是一種極其危險(xiǎn)的操作。
實(shí)際上當(dāng)我們更新了字符集,數(shù)據(jù)庫啟動(dòng)時(shí)會(huì)根據(jù)數(shù)據(jù)庫的字符集自動(dòng)的來修改控制文件的字符集,如果字符集可以識(shí)別,更新控制文
件字符集等于數(shù)據(jù)庫字符集;如果字符集不可識(shí)別,那么控制文件字符集更新為us7ascii.
通過更新props$表的方式修改字符集,在oracle7之后就不應(yīng)該被使用.
以下是我的測(cè)試結(jié)果,但是嚴(yán)禁一切不備份的修改研究,即使是對(duì)測(cè)試庫的。
sql> update props$ set value$='eygle' where name='nls_characterset';1 row updated.sql> commit;commit complete.sql> select name,value$ from props$ where name like '%nls%';name value$------------------------------ -----------------------------------nls_language americannls_territory americanls_currency $nls_iso_currency americanls_numeric_characters .,nls_characterset eyglenls_calendar gregoriannls_date_format dd-mon-rrnls_date_language american….nls_nchar_characterset zhs16gbknls_rdbms_version 8.1.7.1.118 rows selected.重新啟動(dòng)數(shù)據(jù)庫,發(fā)現(xiàn)alert.log文件中記錄如下操作:mon nov 03 16:11:35 2003updating character set in controlfile to us7asciicompleted: alter database open啟動(dòng)數(shù)據(jù)庫后恢復(fù)字符集設(shè)置:sql> update props$ set value$='zhs16gbk' where name='nls_characterset';1 row updated.sql> commit;commit complete.sql> select name,value$ from props$ where name like '%nls%';name value$------------------------------ -----------------------------------nls_language americannls_territory americanls_currency $nls_iso_currency americanls_numeric_characters .,nls_characterset zhs16gbknls_calendar gregoriannls_date_format dd-mon-rrnls_date_language american………nls_comp binarynls_nchar_characterset zhs16gbknls_rdbms_version 8.1.7.1.118 rows selected.重新啟動(dòng)數(shù)據(jù)庫后,發(fā)現(xiàn)控制文件的字符集被更新:mon nov 03 16:21:41 2003updating character set in controlfile to zhs16gbkcompleted: alter database open
理解了字符集調(diào)整的內(nèi)部操作以后,我們可以輕易的指出,以上的方法是不正確的,通過前面 ” alter database character set” 方式更改字
符集時(shí),oracle至少需要更改12張數(shù)據(jù)字典表,而這種直接更新props$表的方式只完成了其中十二分之一的工作,潛在的完整性隱患是可想而知的。
所以,更改字符集盡量要使用正常的途徑。
,歡迎訪問網(wǎng)頁設(shè)計(jì)愛好者web開發(fā)。