循序渐进Oracle:数据库管理、优化与备份恢复
上QQ阅读APP看书,第一时间看更新

3.12 更改字符集的内部方式

前面我们曾经提到,通过修改props$的方式更改字符集在Oracle 7之后是一种极其危险的方式,应该尽量避免。我们又知道,通过ALTER DATABASE CHARACTER SET更改字符集虽然安全可靠,但是有严格的子集和超集的约束,实际上很少能够用到这种方法。

除了前面提到的几种方法外,Oracle 还存在另外一种更改字符集的方式:通过隐含的内部命令强制修改字符集。当使用模板的种子数据库创建数据库时,在创建脚本中可以看到这样的一系列命令:

alter system enable restricted session;

alter database "eygle" open resetlogs;

alter database rename global_name to "eygle";

ALTER TABLESPACE TEMP ADD TEMPFILE 'C:\oracle\oradata\eygle\TEMP01.DBF' SIZE 20480K REUSE AUTOEXTEND ON NEXT 640KMAXSIZE UNLIMITED;

select tablespace_name from dba_tablespaces where tablespace_name='USERS';

select sid, program, serial#, username from v$session;

alter database character set INTERNAL_CONVERT ZHS16GBK;

alter database national character set INTERNAL_CONVERT AL16UTF16;

alter user sys identified by "&&sysPassword";

alter user system identified by "&&systemPassword";

alter system disable restricted session;

在这里面,可以看到这样两条重要的、Oracle非公开的命令:

alter database character set INTERNAL_CONVERT ZHS16GBK;

alter database national character set INTERNAL_CONVERT AL16UTF16;

这个命令是当选择了使用模板方式创建了种子数据库以后,Oracle 会根据选择的字符集设置,把当前种子数据库的字符集更改为期望字符集,这就是这条命令的作用。

如果检查警告日志文件,可以发现相关更改操作的痕迹:

alter database character set INTERNAL_CONVERT ZHS16GBK

Updating character set in controlfile to ZHS16GBK

SYS.SNAP$ (REL_QUERY) - CLOB representation altered

SYS.METASTYLESHEET (STYLESHEET) - CLOB representation altered

SYS.EXTERNAL_TAB$ (PARAM_CLOB) - CLOB representation altered

XDB.XDB$RESOURCE (SYS_NC00027$) - CLOB representation altered

ODM.ODM_PMML_DTD (DTD) - CLOB representation altered

OE.WAREHOUSES (SYS_NC00003$) - CLOB representation altered

PM.ONLINE_MEDIA (SYS_NC00042$) - CLOB representation altered

PM.ONLINE_MEDIA (SYS_NC00062$) - CLOB representation altered

PM.ONLINE_MEDIA (PRODUCT_text) - CLOB representation altered

PM.ONLINE_MEDIA (SYS_NC00080$) - CLOB representation altered

PM.PRINT_MEDIA (AD_SOURCEtext) - CLOB representation altered

PM.PRINT_MEDIA (AD_FINALtext) - CLOB representation altered

Completed: alter database character set INTERNAL_CONVERT ZHS1

在使用这个命令时,Oracle 会跳过所有子集及超集的检查,在任意字符集之间进行强制转换,所以,使用这个命令时你必须十分小心,并必须清楚这一操作可能带来的风险。

之前讲过的内容仍然有效,可以使用csscan扫描整个数据库,如果在转换的字符集之间确认没有严重的数据损坏,或者可以使用有效的方式更改,就可以使用这种方式进行转换。这个内部命令的用户引用格式如下:

alter database character set INTERNAL_USE ZHS16CGB231280

来看一下具体的操作过程及Oracle的内部操作:

SQL> shutdown immediate

Database closed.

Database dismounted.

ORACLE instance shut down.

SQL> STARTUP MOUNT;

Database mounted.

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> alter session set events '10046 trace name contextforever,level 12';

Session altered.

SQL> alter database character set INTERNAL_USE ZHS16CGB231280

Database altered.

这时警告日志文件中的记录了以下信息:

Tue Oct 19 16:26:30 2004

Database Characterset is ZHS16GBK

replication_dependency_tracking turned off (no async multimaster replication found)

Completed: ALTER DATABASE OPEN

Tue Oct 19 16:27:07 2004

alter database character set INTERNAL_USE ZHS16CGB231280

Updating character set in controlfile to ZHS16CGB231280

Tue Oct 19 16:27:15 2004

Thread 1 advanced to log sequence 118

Current log# 2 seq# 118 mem# 0: /opt/oracle/oradata/primary/redo02.log

Tue Oct 19 16:27:15 2004

ARC0: Evaluating archive log 3 thread 1 sequence 117

ARC0: Beginning to archive log 3 thread 1 sequence 117

Creating archive destination LOG_ARCHIVE_DEST_1: '/opt/oracle/oradata/primary/archive/1_117.dbf'

ARC0: Completed archiving log 3 thread 1 sequence 117

Tue Oct 19 16:27:20 2004

Completed: alter database character set INTERNAL_USE ZHS16CGB231280

Shutting down instance: further logons disabled

Shutting down instance (immediate)

License high water mark = 1

Tue Oct 19 16:29:06 2004

ALTER DATABASE CLOSE NORMAL

……

格式化10046跟踪文件,得到以下信息(摘要):

alter session set events '10046 trace name contextforever,level 12'

alter database character set INTERNAL_USE ZHS16CGB231280

....

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'

....

ALTER DATABASE CLOSE NORMAL

可以看到这个过程和之前ALTER DATABASE CHARACTER SET操作的内部过程是完全相同的,也就是说INTERNAL_USE提供的帮助就是使Oracle数据库绕过了子集与超集的校验。

这一方法在某些方面是有用处的,比如测试;但应用于产品环境时就应该格外小心,除了你以外,没有人会为此带来的后果负责!