|
目前Oracle对中文的支持已经做的很好,有很多字符集可以存储中文字符,常用的有ZHS16GBK、UTF8等等。而对于除UTF8字符集外,其他字符集在存储中文字符时将会使用两个字节,但如果使用UTF8,则需要占用3个字节的空间。因此,问题来了,如果我们将两个字节的字符集中的数据导入UTF8字符集时是否会出现问题?怎么解决该问题?
我们先来做个实验:
D:\>sqlplus /nolog
SQL*Plus: Release 8.1.7.0.0 - Production on 星期一 3月 28 15:02:56 2005
(c) Copyright 2000 Oracle Corporation. All rights reserved.
SQL> connect ca_user/ca_user@cost1[/COLOR]
已连接。
SQL> select *
2 from v$nls_parameters;
PARAMETER VALUE
----------------------------------------------
NLS_LANGUAGE SIMPLIFIED CHINESE
NLS_TERRITORY CHINA
NLS_CURRENCY RMB
NLS_ISO_CURRENCY CHINA
NLS_NUMERIC_CHARACTERS .,
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE SIMPLIFIED CHINESE
NLS_CHARACTERSET ZHS16GBK[/COLOR]
NLS_SORT BINARY
NLS_TIME_FORMAT HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM TZH:TZM
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM TZH:TZM
NLS_DUAL_CURRENCY RMB
NLS_NCHAR_CHARACTERSET ZHS16GBK
NLS_COMP BINARY
已选择17行。
SQL> select status from alac_accounting_periods
2 where rownum=1;
STATUS
--------------------
关闭
SQL> select dump(status) from alac_accounting_periods
2 where rownum=1;
DUMP(STATUS)
-----------------------------------------------------------------------
Typ=1 Len=4: 185,216,177,213[/COLOR]
这里我们看出,字串“关闭”在ZHS16GBK字符集中占用了4个字节。
SQL> connect ca_user/ca_user@ebsse[/COLOR] 已连接。
SQL> select *
2 from v$nls_parameters;
PARAMETER VALUE
----------------------------------------------
NLS_LANGUAGE SIMPLIFIED CHINESE
NLS_TERRITORY CHINA
NLS_CURRENCY RMB
NLS_ISO_CURRENCY CHINA
NLS_NUMERIC_CHARACTERS .,
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE SIMPLIFIED CHINESE
NLS_CHARACTERSET UTF8[/COLOR]
NLS_SORT BINARY
NLS_TIME_FORMAT HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM TZH:TZM
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM TZH:TZM
NLS_DUAL_CURRENCY RMB
NLS_NCHAR_CHARACTERSET AL16UTF16
NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTE
NLS_NCHAR_CONV_EXCP FALSE
已选择19行。
SQL> select status from alac_accounting_periods
2 where rownum=1;
STATUS
------------------------------------------------------------
关闭
SQL> select dump(status) from alac_accounting_periods
2 where rownum=1;
DUMP(STATUS)
-----------------------------------------------------------------------
Typ=1 Len=6: 229,133,179,233,151,173[/COLOR]
这里可以看出,相同的字串,在UTF8字符集里占用了6个字节。
我们可以预测,如果将ZHS16GBK的数据进行导出,并且导入UTF8字符集的话,则有可能原先可以存储在ZHS16GBK字符集下的字串因为实际占用字节增加而超过列定义的大小,导致导入失败。
Oracle提供了一个检测字符集冲突的工具csscan:http://www.oracle.com/technology ... cs/utilsoft.html#10
利用这个工具可以检查出那些数据在进行字符集转换时可能会出问题。这里不再祥述。
继续我们的实现,看看是否会出现刚才预测的问题。在导入的过程中确实出现了错误:
IMP-00019: 行被拒绝是因为 ORACLE 错误1401
IMP-00003: ORACLE 错误1401出现
ORA-01401: 插入的值对于列过大
列 1 012
列 2 E54
列 3
列 4 05-20
列 5 200405
列 6 1
列 7 14005
列 8 B747-200F
列 9 52155.58
列 10 52155.58
列 11 外航新开帐单[/COLOR]
列 12
再看看这个:
SQL> desc ALAC_ACCA_CR_SNAP
名称 空? 类型
----------------------------------------- -------- --------------
开帐公司 VARCHAR2(20)
被开帐公司 VARCHAR2(20)
航站 VARCHAR2(3)
帐单号 VARCHAR2(30)
外航清算月 VARCHAR2(50)
序号 NUMBER
服务项目 VARCHAR2(15)
机型 VARCHAR2(10)
帐单金额 NUMBER
接受金额 NUMBER
CASE VARCHAR2(12)[/COLOR]
SNAP_MONTH VARCHAR2(6)
我们发现第11列,即CASE列的大小为12,如果在两字节字符集中,刚好能够存储,但在导入UTF8时将会出错。
ORACLE官方的解决方案是:利用csscan来查找,然后更改表的定义。但是这个方法比较麻烦。有没有简单点的方法,对DMP文件动动手脚,让他能够导入呢?
既然是由于列定义大小过小而导致此问题,那么应该可以修改DMP文件中CREATE TABLE SQL中对于字符串列定义的大小,从而能够直接导入修改后的DMP文件。
常用的字符串类型有:Varchar2,Nvarchar2,char等,在本例中,中文字符都存储在varchar2类型中,所以只需对varchar2进行处理。我们需要读取DMP文件中varchar2大小T,然后将T*2/3向上取整后的数字写回DMP文件即可。这里附加上我用Delphi写的处理程序。
相关工具下载:
http://sundog.icpcn.com/tempfile/DMPUTF8.rar |
|