双字节中文字符集导出的DMP文件导入UTF8字符集

    目前Oracle对中文的支持已经做的很好,有很多字符集可以存储中文字符,常用的有ZHS16GBK、UTF8等等。而对于除UTF8字符集外,其他字符集在存储中文字符时将会使用两个字节,但如果使用UTF8,则需要占用3个字节的空间。因此,问题来了,如果我们将两个字节的字符集中的数据导入UTF8字符集时是否会出现问题?怎么解决该问题?[@more@]

    我们先来做个实验:

    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
    已连接。

    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
    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
   
    这里我们看出,字串“关闭”在ZHS16GBK字符集中占用了4个字节。

    SQL> connect ca_user/ca_user@ebsse
    已连接。

    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
    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

    这里可以看出,相同的字串,在UTF8字符集里占用了6个字节。
    我们可以预测,如果将ZHS16GBK的数据进行导出,并且导入UTF8字符集的话,则有可能原先可以存储在ZHS16GBK字符集下的字串因为实际占用字节增加而超过列定义的大小,导致导入失败。
    Oracle提供了一个检测字符集冲突的工具csscan:http://www.oracle.com/technology/software/tech/globalization/htdocs/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 外航新开帐单
    列 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)
    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.go1.icpcn.com/tempfile/DMPUTF8.rar

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/19423/viewspace-794183/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/19423/viewspace-794183/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值