在"解决Multibyte character error一例"这篇文章里,我最后用到的解决方法是set NLS_LANG=AMERICAN_AMERICA.WE8ISO8859P1,有同事看到这篇文章后问我为什么要这么做?原因是什么?
我这里做一下说明,之所以有了上述解决方法是因为我曾经看到227330.1里有这样一段话"Files made on Unix systems (using vi for example) are most of the time WE8ISO8859P1 depending on the LOCALE setting or used telnet/ssh config."
下面我以一个例子来说明上述问题:
首先来重现一下问题:
E:\>sqlldr ipra/acca@ipradev control=ATPCO.ctl data=LKTCN081121.bad
SQL*Loader: Release
Copyright (c) 1982, 2005, Oracle. All rights reserved.
这里sqlldr实际上是一条数据都没有导入进去。
这里的本质原因是因为这里windows客户端默认的NLS_LANG是SIMPLIFIED CHINESE_CHINA.ZHS16GBK,而ANSI编码方式中又没有A5所对应的字符,所以oracle这里报错Multibyte character error。
WE8ISO8859P1是很常用的西欧字符集,我当时估计里面肯定是有A5的,而且当时我的源数据文件中并没有中文,所以我这里放心的将NLS_LANG设成AMERICAN_AMERICA.WE8ISO8859P1,目的就是告诉oracle我这里A5不是ANSI编码,WE8ISO8859P1有A5,你帮我在WE8ISO8859P1和ZHS16GBK之间做一个转换吧。
E:\>set NLS_LANG=AMERICAN_AMERICA.WE8ISO8859P1
E:\>sqlldr ipra/acca@ipradev control=ATPCO.ctl data=LKTCN081121.bad
SQL*Loader: Release
Copyright (c) 1982, 2005, Oracle. All rights reserved.
Commit point reached - logical record count 2
转换后我们来看一下转换后A5变成了什么:
SQL> select substr(oatint,314,1) from owbatptemp;
SUBSTR(OATINT,314,1)
--------------------
¥
¥
原来我们待导入的字符是A5,也就是¥。
现在导入完成后变成了¥。
用ultraEdit看了一下这个¥的16进制码,ultraEdit显示这个¥的16进制码是A3 A4,我们来验证一下 :
SQL> select dump(substr(oatint,314,1)) from owbatptemp;
DUMP(SUBSTR(OATINT,314,1))
--------------------------------------------------------------------------------
Typ=1 Len=2: 163,164
Typ=1 Len=2: 163,164
这里的163,164就是A3和A4:
SQL> select to_char(163,'XX') from dual;
TO_CHAR(163,'XX')
-----------------
A3
SQL> select to_char(164,'XX') from dual;
TO_CHAR(164,'XX')
-----------------
A4
我们可以很清晰的看到这里实际上是发生了字符集的转换。
注意这里如果待导入的源数据中有中文,这种方式就不可以采用!一旦采用,所有的中文都会变成乱码,而且不可逆!原因就不用我说了吧:)
将LKTCN081121.bad的最开始插入两个中文字符"崔华",然后再次以同样方式导入,我们来看一下结果:
E:\>sqlldr ipra/acca@ipradev control=ATPCO.ctl data=LKTCN081121.bad
SQL*Loader: Release
Copyright (c) 1982, 2005, Oracle. All rights reserved.
Commit point reached - logical record count 2
QL> select substr(oatint,1,10) from owbatptemp;
SUBSTR(OATINT,1,10)
--------------------
′T?a070006
′T?a070006
0700060842
0700060851
好了,我来总结一下:
1、WE8ISO8859P1不是unicode字符集,它是ISO西欧字符集之一,它不是ZHS16GBK的超集。
2、如果待导入的源数据中有中文,就不能采用我上述的解决方式。
Leave a comment