查看: 8405|回复: 8

[原创] 双字节中文字符集导出的DMP文件导入UTF8字符集

[复制链接]
论坛徽章:
86
2015中国数据库技术大会纪念徽章
日期:2015-04-24 16:04:24马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11优秀写手
日期:2013-12-18 09:29:11日产
日期:2013-10-17 08:44:39马自达
日期:2013-08-26 16:28:022013年新春福章
日期:2013-02-25 14:51:24ITPUB 11周年纪念徽章
日期:2012-10-23 16:55:51马上有房
日期:2014-02-19 11:55:14
跳转到指定楼层
1#
发表于 2005-3-28 16:05 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
目前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

dmputf8.rar

148.27 KB, 下载次数: 266

论坛徽章:
0
2#
发表于 2005-3-28 16:24 | 只看该作者
谢谢,收藏。

使用道具 举报

回复
论坛徽章:
86
2015中国数据库技术大会纪念徽章
日期:2015-04-24 16:04:24马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11优秀写手
日期:2013-12-18 09:29:11日产
日期:2013-10-17 08:44:39马自达
日期:2013-08-26 16:28:022013年新春福章
日期:2013-02-25 14:51:24ITPUB 11周年纪念徽章
日期:2012-10-23 16:55:51马上有房
日期:2014-02-19 11:55:14
3#
 楼主| 发表于 2005-3-30 11:27 | 只看该作者
增加一个C代码

dmputf8.txt

3.78 KB, 下载次数: 162

使用道具 举报

回复
论坛徽章:
25
奥运会纪念徽章:射击
日期:2013-01-28 09:12:182014年新春福章
日期:2014-02-18 16:41:11马上有车
日期:2014-02-18 16:41:11马上有车
日期:2014-03-20 16:13:24马上有房
日期:2014-03-20 16:14:11马上有钱
日期:2014-03-20 16:14:11马上有对象
日期:2014-03-20 16:14:11马上加薪
日期:2014-03-20 16:14:11喜羊羊
日期:2015-04-09 18:46:34秀才
日期:2016-03-24 09:20:52
4#
发表于 2010-7-15 16:05 | 只看该作者
赞一个

使用道具 举报

回复
论坛徽章:
12
2009新春纪念徽章
日期:2009-01-04 14:52:28优秀写手
日期:2014-06-20 06:00:12蛋疼蛋
日期:2012-02-22 08:52:48ITPUB十周年纪念徽章
日期:2011-11-01 16:24:04生肖徽章2007版:鸡
日期:2009-11-11 13:17:35祖国60周年纪念徽章
日期:2009-10-09 08:28:00ITPUB8周年纪念徽章
日期:2009-09-27 10:21:21生肖徽章2007版:龙
日期:2009-08-09 19:39:29生肖徽章2007版:鸡
日期:2009-08-03 23:22:352009日食纪念
日期:2009-07-22 09:30:00
5#
发表于 2010-7-20 12:39 | 只看该作者
你可否改改
这个nls_length_semantics这个参数试试

使用道具 举报

回复
论坛徽章:
4
ITPUB十周年纪念徽章
日期:2011-11-01 16:26:292012新春纪念徽章
日期:2012-01-04 11:57:562014年新春福章
日期:2014-02-18 16:44:08马上有对象
日期:2014-02-18 16:44:08
6#
发表于 2012-2-6 22:12 | 只看该作者
本帖最后由 oradbguy 于 2012-2-6 22:12 编辑

T*2/3向上取整后的数字写回DMP文件

有个问题,如果原来就是定义的varchar2(4000) 就不好办了。
不知道能不能直接改dump里面的 建表为clob

使用道具 举报

回复
论坛徽章:
86
2015中国数据库技术大会纪念徽章
日期:2015-04-24 16:04:24马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11优秀写手
日期:2013-12-18 09:29:11日产
日期:2013-10-17 08:44:39马自达
日期:2013-08-26 16:28:022013年新春福章
日期:2013-02-25 14:51:24ITPUB 11周年纪念徽章
日期:2012-10-23 16:55:51马上有房
日期:2014-02-19 11:55:14
7#
 楼主| 发表于 2012-2-7 13:59 | 只看该作者
oradbguy 发表于 2012-2-6 22:12
T*2/3向上取整后的数字写回DMP文件

有个问题,如果原来就是定义的varchar2(4000) 就不好办了。

当时的环境没有太大的varchar2定义,其实,主要是明白2字节和3字节的区别就行。解决方法有很多种

使用道具 举报

回复
论坛徽章:
2
生肖徽章2007版:兔
日期:2011-01-20 12:58:492011新春纪念徽章
日期:2011-02-18 11:43:35
8#
发表于 2012-2-24 13:18 | 只看该作者
目前正需要这样的材料,方法挺好,但是dmp文件很大的时候,是否有更合适的办法。

使用道具 举报

回复
论坛徽章:
47
蒙奇·D·路飞
日期:2017-03-27 08:04:23马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11一汽
日期:2013-09-01 20:46:27复活蛋
日期:2013-03-13 07:55:232013年新春福章
日期:2013-02-25 14:51:24ITPUB 11周年纪念徽章
日期:2012-10-09 18:03:322012新春纪念徽章
日期:2012-02-13 15:13:202012新春纪念徽章
日期:2012-02-13 15:13:202012新春纪念徽章
日期:2012-02-13 15:13:20
9#
发表于 2012-2-24 22:14 | 只看该作者
本帖最后由 Yong Huang 于 2012-2-24 11:15 编辑

Directly modifying the dump file is a risky operation and may nullify support. I would still follow Oracle's suggestion. We can generate DDL's to widen the columns according to the scan.err file from csscan, which has lines like the following:


User  : SMARTSTREAM
Table : GL_DETAIL_FY11
Column: DESC_3
Type  : VARCHAR2(10)
Number of Exceptions         : 75
Max Post Conversion Data Size: 17


Construct the DDL's as follows. Take the above as an example:

alter table SMARTSTREAM.GL_DETAIL_FY11 modify DESC_3 VARCHAR2(17);

Then you pre-create the tables in the target database with no rows and then import with rows. You can automate DDL generation with my csscan2ddl.pl:
http://yong321.freeshell.org/oranotes/csscan.txt

Yong Huang

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表