My studying notes for Java,Ruby,Ajax and other any interesting things.

星期六, 十一月 04, 2006

Oracle不同字符集间的imp/exp问题

新装了了一个机器,想把以前的oracle数据导过来,部署一个新的应用。数据库版本都是10.2.0,但是字符集不同。老的字符集是zh32gbk,新的字符集为al16utf8,导入表的过程基本顺利,但是数据库的的数据则不然了。

在导入数据库的时候总是报告部分列的内容超过最大值,看了一下,也不是所谓的clob或者blob造成的。初步分析以后发现应该是utf8里面的一个汉字占用三个字节的宽度,在gbk里面占用则为两个字节。也就是对于相同长度的汉字在gbk字符集下面比在utf8下面占用的长度较小。但是由于在imp和exp的过程中并不会修改表里面的列的长度,因此在从gbk的数据库里面到出来的数据无法导入导utf8里面去。

解决办法有两个:
1、修改字符集,将utf8的字符集数据库修改成为gbk。这个办法看似简单实则不然,因为utf8字符集本身是gbk的超集,想要将其修改为子集,oracle是不允许的。因此只能够删掉数据库重新创建数据库,对于一个新的数据库来说可能开销不大,但是如果是一个运行了较长时间有一定数据的数据库来说就有点惨了。这个办法还有一个缺点,因为utf8的目标是存储所有的utf字节,也就是说可以存储诸如日文,韩文和中文等多字节的文字,但是如果将字符集修改成为了gbk的话就只能存储中文字符和单字节的字符了,这对于国际化来说并不是什么好事。

2、修改表结构。将表结构中的varchar2或者char等数据类型的列都适当的扩展,长度扩展为3/2或者2倍即可。这个办法是个好办法,但是对于数据库表较多的情况来说也是相当麻烦的一件事情。不过幸好可以写一些程序来统一处理这样的问题,可以先通过plsq或者toad或者类似的工具将表结构导出(没有数据)成为纯文本的sql脚本,然后将其中的会受到国际化影响的数据库字段类型的长度进行相应的扩充。


当然,由于我是新建的数据库,而且暂时不用考虑到国际化的问题,因此我选用了第一种办法。

在查找资料的过程中也找到一些相关修改数据库字符集的资料:

三、用dblink在10G中做create table ..as select * from ..varchar2()的column宽度变成3倍
  问题描述:例如:varchar2(255)==>varchar(765)

  测试:1. 环境: 两台机器都装的10G的数据库
  2. 测试步骤和结果:

SQL> conn test1/test1@db10G_server1
SQL> create database link link_server2

connect to test1 identified by test1

using 'db10G_server2'
SQL> conn test1/test1@db10G_server2
SQL> create table test1_t1 as select * from all_objects

where rownum<=3000;
SQL> desc test1_t1;
Name Null? Type

---------------------------------------------------------------

OWNER VARCHAR2(30)

OBJECT_NAME VARCHAR2(30)

SUBOBJECT_NAME VARCHAR2(30)

OBJECT_ID NUMBER

DATA_OBJECT_ID NUMBER

OBJECT_TYPE VARCHAR2(19)

CREATED DATE

LAST_DDL_TIME DATE

TIMESTAMP VARCHAR2(19)

STATUS VARCHAR2(7)

TEMPORARY VARCHAR2(1)

GENERATED VARCHAR2(1)

SECONDARY VARCHAR2(1)
SQL> conn test1/test1@db10G_server1
SQL> create table test1_linkt1 as

select * from test1_t1@link _server2;
SQL> desc test1_linkt1
Name Null? Type

----------------------------------------- -------- ----------------

OWNER VARCHAR2(60)

OBJECT_NAME VARCHAR2(60)

SUBOBJECT_NAME VARCHAR2(60)

OBJECT_ID NUMBER

DATA_OBJECT_ID NUMBER

OBJECT_TYPE VARCHAR2(38)

CREATED DATE

LAST_DDL_TIME DATE

TIMESTAMP VARCHAR2(38)

STATUS VARCHAR2(14)

TEMPORARY VARCHAR2(2)

GENERATED VARCHAR2(2)

SECONDARY VARCHAR2(2)


  表结构一样,但是列的长度确实变了, 通过dblink创建的表的varchar2列的长度是原表的两倍

  再来看看字符集

  1.Server1上的数据库字符集

SQL> column value format A20
SQL> select value from nls_database_parameters where parameter='NLS_CHARACTERSET';

value
--------------
ZHS16GBK



  2. Server2上的数据库字符集

SQL> conn test1/test1@db10G_server2
SQL> column value format A20
SQL> select value from nls_database_parameters where parameter='NLS_CHARACTERSET';

value
--------------
AL32UTF8


  结论: 与数据库字符集不同有关系.



中科院一篇讨论oracle字符集的文章:
http://www.csdb.cn/viewPaper.jsp?tipid=1148536745101
修改oracle数据库字符集的方法:
http://www.cg163.net/ithtm/id.aspid=17738.htm
<我从utf8修改到gbk失败,因为oracle报告错误说只能修改成为超集:ORA-12712: 新字符集必须为旧字符集的超集>


--
----------------------------------

   你的支持 我的坚持
  Lead to The IT Future

----------------------------------

没有评论: