3.5 导入导出及字符转换
当进行数据导入时,主要存在以下两种情况。
源数据库和目标数据库具有相同字符集设置。
这时,只需要设置NLS_LANG等于数据库字符集即可导入(前提是,导出使用的是和源数据库相同字符集,即三者相同)。
源数据库和目标数据库字符集不同。
如果导出时使用的 NLS_LANG 是和源数据库相同的字符集,那么导入时就可以设置客户端NLS_LANG等于导出时使用的字符集,这样转换只发生在数据库端,而且只发生一次。
例如,进行从WE8MSWIN1252到UTF8的转换,以下是一个常用的参考步骤。
(1)使用NLS_LANG=AMERICAN_AMERICA.WE8MSWIN1252导出数据库。这时创建的导出文件包含WE8MSWIN1252的数据。
(2)导入时使用 NLS_LANG=AMERICAN_AMERICA.WE8MSWIN1252。这时转换仅发生在 insert数据到UTF8的数据库中。
以上假设的转换只在目标数据库字符集是源数据库字符集的超集时才能转换。如果不同,一般就需要进行一些特殊的处理。
再来看一下执行导入时Oracle的一些判断和处理过程(以Oracle 8i为例):
(1)首先确定导出数据库字符集环境。通过读取导出文件头,可以获得导出文件的字符集设置。
(2)确定导入session的字符集,即导入Session使用的NLS_LANG环境变量。
(3)通过IMP读取导出文件。读取导出文件字符集ID,和导入进程的NLS_LANG进行比较。
(4)如果导出文件字符集和导入Session字符集相同,那么在这一步骤内就不需要转换,如果不同,就需要把数据转换为导入Session使用的字符集。然而这种转换只能在单byte字符集之间进行。
来看一个测试,首先设置导入session NLS_LANG为US7ASCII:
E:\nls2>set NLS_LANG=AMERICAN_AMERICA.US7ASCII
执行导入操作:
E:\nls2>e:\oracle\ora8i\bin\imp eygle/eygle file=Sus7ascii-Cus7ascii-exp817.dmp fromuser=eygle touser=eygletables=test
这个导出文件是从US7ASCII数据库导出,导出客户端NLS_LANG也是US7ASCII:
Import: Release 8.1.7.1.1 - Production on Fri Nov 7 00:59:22 2003
(c) Copyright 2000 Oracle Corporation. All rights reserved.
Connected to: Oracle8i Enterprise Edition Release 8.1.7.1.1 - Production
With the Partitioning option
JServer Release 8.1.7.1.1 - Production
这时导入,在 DMP 文件和 NLS_LANG 之间不需要进行字符集转换,但是由于导出文件字符集和数据库字符集(ZHS16GBK)不同,在数据导入Server时需要进行转换。
Export file created by EXPORT:V08.01.07 via conventional path
import done in US7ASCII character set and ZHS16GBK NCHAR character set
import server uses ZHS16GBK character set (possible charset conversion)
export server uses UTF8 NCHAR character set (possible ncharset conversion)
. . importing table "TEST" 2 rows imported
Import terminated successfully without warnings.
(5)对于多Byte字符集的导入(如UTF8),需要设置导入Session字符集和导出字符集相同,否则就会遇到以下错误:IMP-16 "Required character set conversion (type %lu to %lu) not supported"。
E:\nls2>set NLS_LANG=AMERICAN_AMERICA.ZHS16GBK
导入 Session字符集设置为 ZHS16GBK,导入US7ASCII的导出文件:
E:\nls2>e:\oracle\ora8i\bin\imp eygle/eygle file=Sus7ascii-Cus7ascii-exp817.dmp fromuser=eygle touser=eygle
Import: Release 8.1.7.1.1 - Production on Fri Nov 7 00:38:55 2003
(c) Copyright 2000 Oracle Corporation. All rights reserved.
Connected to: Oracle8i Enterprise Edition Release 8.1.7.1.1 - Production
With the Partitioning option
JServer Release 8.1.7.1.1 - Production
IMP-00016: required character set conversion (type 1 to 852) not supported
IMP-00000: Import terminated unsuccessfully
在从导出文件US7ASCII到导入NLS_LANG设置为ZHS16GBK的过程中,不支持单Byte字符集向多Byte转换,报出以上错误。
(6)导入Session字符集应该是导出字符集的超集,否则,专有的字符将难以正确转换。
(7)当数据转换为导入Session字符集设置以后,如果导入Session字符集仍然不同于目标数据库字符集,那么数据在插入数据库之前还需要进行最后一步转换,这要求目标数据库字符集是导入session字符集的超集,否则某些专有字符将不能正常转换。
继续看上面的两个过程,这里有这样两个原则。
如果 NLS_LANG 的设置和数据库相同,那么数据(在传输过程中当然是二进制码)不经过转换就直接插入数据库中。
如果NLS_LANG的设置和数据库不同,那么数据需要转换后才能插入数据库中。
再回头来看上面的第一个例子:
Export file created by EXPORT:V08.01.07 via conventional path
import done in US7ASCII character set and ZHS16GBK NCHAR character set
import server uses ZHS16GBK character set (possible charset conversion)
export server uses UTF8 NCHAR character set (possible ncharset conversion)
. . importing table "TEST" 2 rows imported
Import terminated successfully without warnings.
这时候经过第一步转换后的数据,US7ASCII到ZHS16GBK丢失首位,原样插入数据库,可以看到这时数据库中存放的就是错误的字符(在后面部分做了详细的转换):
E:\nls2>sqlplus eygle/eygle
Connected to:Oracle8i Enterprise Edition Release 8.1.7.1.1 - Production
With the Partitioning option
JServer Release 8.1.7.1.1 - Production
SQL> select * from test;
NAME
--------------------
2bJT
test
对于Oracle 10g开始引入的数据泵(expdp/impdp)工具,不再存在以前EXP/IMP的字符集转换问题,expdp/impdp 实际上是通过工具在数据库服务器上提交一个任务,真正的导出及导入操作都是在数据库服务器上完成的,这就简化了Oracle的数据转储工作。