字符集問題的初步探討
2024-07-21 02:40:17
供稿:網友
2. 字符集的更改
數據庫創建以后,假如需要修改字符集,通常需要重建數據庫,通過導入導出的方式來轉換。
我們也可以通過以下方式更改
ALTER DATABASE CHARACTER SET
注重:修改數據庫字符集時必須謹慎,修改之前一定要為數據庫備份。由于不能回退這項操作,因此可能會造成數據丟失或者損壞。
這是最簡單的轉換字符集的方式,但并不總是有效。
這個命令在Oracle8時被引入Oracle,這個操作在本質上并不轉換任何數據庫字符,只是簡單的更新數據庫中所有跟字符集相關的信息。
這意味著,你只能在新字符集是舊字符集嚴格超集的情況下使用這種方式轉換。
所謂超集是指:
當前字符集中的每一個字符在新字符集中都可以表示,并使用同樣的代碼點
比如很多字符集都是US7ASCII的嚴格超集。
假如不是超集,將獲得以下錯誤:
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
下面我們來看一個測試(以下測試在Oracle9.2.0下進行,Oracle9i較Oracle8i在編碼方面有較大改變,在Oracle8i中,測試結果可能略有不同):
SQL> select name,value$ from PRops$ where name like ’%NLS%’;
NAME VALUE$
------------------------------ ------------------------------
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CURRENCY $
NLS_ISO_CURRENCY AMERICA
NLS_NUMERIC_CHARACTERS .,
NLS_CHARACTERSET US7ASCII
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE AMERICAN
……………….
NLS_NCHAR_CHARACTERSET AL16UTF16
NLS_RDBMS_VERSION 9.2.0.4.0
20 rows selected.
SQL> select name,dump(name) from eygle.test;
NAME DUMP(NAME)
------------------------------------------------------
測試 Typ=1 Len=4: 178,226,202,212
Test Typ=1 Len=4: 116,101,115,116
2 rows selected.
轉換字符集,數據庫應該在RESTRICTED模式下進行.
c:/>sqlplus "/ as sysdba"
SQL*Plus: Release 9.2.0.4.0 - ProdUCtion on Sat Nov 1 10:52:30 2003
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
Connected to:
Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
With the Partitioning, Oracle Label Security, OLAP and Oracle Data Mining options
JServer Release 9.2.0.4.0 - Production
SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> STARTUP MOUNT;
ORACLE instance started.
Total System Global Area 76619308 bytes
Fixed Size 454188 bytes
Variable Size 58720256 bytes
Database Buffers 16777216 bytes
Redo Buffers 667648 bytes
Database 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 120
SQL> ALTER DATABASE CHARACTER SET ZHS16GBK;
ALTER DATABASE CHARACTER SET ZHS16GBK
*
ERROR at line 1:
ORA-12721: Operation cannot execute when other sessions are active
SQL> 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中,假如數據庫存在CLOB類型字段,那么就不答應對字符集進行轉換
SQL>
這時候,我們可以去查看alert<sid>.log日志文件,看CLOB字段存在于哪些表上:
ALTER DATABASE CHARACTER SET ZHS16GBK SYS.METASTYLESHEET (STYLESHEET) - CLOB populatedORA-12716 signalled during: ALTER DATABASE CHARACTER SET ZHS16GBK...
對于不同情況,Oracle提供不同的解決方案,假如是用戶數據表,一般我們可以把包含CLOB字段的表導出,然后drop掉相關對象,
轉換后再導入數據庫;對于系統表,可以按照以下方式處理:
SQL> truncate table Metastylesheet;
Table truncated.
然后可以繼續進行轉換!
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中,轉換完成以后,可以通過運行catmet.sql腳本來重建Metastylesheet表:
SQL> @?/rdbms/admin/catmet.sql
轉換后的數據:
SQL> select name,value$ from props$ where name like ’%NLS%’;
NAME VALUE$
------------------------------ ------------------------------
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CURRENCY $
NLS_ISO_CURRENCY AMERICA
NLS_NUMERIC_CHARACTERS .,
NLS_CHARACTERSET ZHS16GBK
…..
NLS_NCHAR_CHARACTERSET AL16UTF16
NLS_RDBMS_VERSION 9.2.0.4.0
20 rows selected.
SQL> select * from eygle.test;
NAME
------------------------------
測試
test
2 rows selected.
提示:
通過設置sql_trace,我們可以跟蹤很多數據庫的后臺操作,這個工具是DBA常用的“利器”之一。
我們簡單看一下數據庫更改字符集時的后臺處理,我提取了主要的更新部分。
通過以下跟蹤過程,我們看到數據庫在更改字符集的時候,主要更新了12張數據字典表,修改了數據庫的原數據,這也證實了我們以前的說法:
這個更改字符集的操作在本質上并不轉換任何數據庫字符,只是簡單的更新數據庫中所有跟字符集相關的信息。
update col$ set charsetid = :1
where
charsetform = :2
update argument$ set charsetid = :1
where
charsetform = :2
update collection$ set charsetid = :1
where
charsetform = :2
update attribute$ set charsetid = :1
where
charsetform = :2
update parameter$ set charsetid = :1
where
charsetform = :2
update result$ set charsetid = :1
where
charsetform = :2
update partcol$ set spare1 = :1
where
charsetform = :2
update subpartcol$ set spare1 = :1
where
charsetform = :2
update props$ set value$ = :1
where
name = :2
update "SYS"."KOTAD$" set SYS_NC_ROWINFO$ = :1
where
SYS_NC_OID$ = :2
update seq$ set increment$=:2,minvalue=:3,maxvalue=:4,cycle#=:5,order$=:6,
cache=:7,highwater=:8,audit$=:9,flags=:10
where
obj#=:1
update kopm$ set metadata = :1, length = :2
where
name=’DB_FDO’
在這里我們順便糾正一個由來以及的錯誤方法.
經常可以在網上看到這樣的更改字符集的方法:
1)用SYS用戶名登陸ORACLE。
2)查看字符集內容
SQL>SELECT * FROM PROPS$;
3)修改字符集
SQL> update props$ set value$=’新字符集’ where name=’NLS_CHARACTERSET’
4) COMMIT;
我們看到很多人在這個問題上碰到了慘痛的教訓,使用這種方式更改字符集,假如你的value$值輸入了不正確的字符集,在8i中那么你
的數據庫可能會無法啟動,這種情況是非常嚴重的,有時候你必須從備份中進行恢復;假如是在9i中,可以重新啟動數據庫后再修改回正
確的字符集。但是我們仍然不建議使用這種方式進行任何數據庫修改,這是一種極其危險的操作。
實際上當我們更新了字符集,數據庫啟動時會根據數據庫的字符集自動的來修改控制文件的字符集,假如字符集可以識別,更新控制文
件字符集等于數據庫字符集;假如字符集不可識別,那么控制文件字符集更新為US7ASCII.
通過更新props$表的方式修改字符集,在Oracle7之后就不應該被使用.
以下是我的測試結果,但是嚴禁一切不備份的修改研究,即使是對測試庫的。
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 AMERICAN
NLS_TERRITORY AMERICA
NLS_CURRENCY $
NLS_ISO_CURRENCY AMERICA
NLS_NUMERIC_CHARACTERS .,
NLS_CHARACTERSET EYGLE
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE AMERICAN
….
NLS_NCHAR_CHARACTERSET ZHS16GBK
NLS_RDBMS_VERSION 8.1.7.1.1
18 rows selected.
重新啟動數據庫,發現alert.log文件中記錄如下操作:
Mon Nov 03 16:11:35 2003
Updating character set in controlfile to US7ASCII
Completed: ALTER DATABASE OPEN
啟動數據庫后恢復字符集設置:
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 AMERICAN
NLS_TERRITORY AMERICA
NLS_CURRENCY $
NLS_ISO_CURRENCY AMERICA
NLS_NUMERIC_CHARACTERS .,
NLS_CHARACTERSET ZHS16GBK
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE AMERICAN
………
NLS_COMP BINARY
NLS_NCHAR_CHARACTERSET ZHS16GBK
NLS_RDBMS_VERSION 8.1.7.1.1
18 rows selected.
重新啟動數據庫后,發現控制文件的字符集被更新:
Mon Nov 03 16:21:41 2003
Updating character set in controlfile to ZHS16GBK
Completed: ALTER DATABASE OPEN
理解了字符集調整的內部操作以后,我們可以輕易的指出,以上的方法是不正確的,通過前面 ” ALTER DATABASE CHARACTER SET” 方式更改字
符集時,Oracle至少需要更改12張數據字典表,而這種直接更新props$表的方式只完成了其中十二分之一的工作,潛在的完整性隱患是可想而知的。
所以,更改字符集盡量要使用正常的途徑。