Tom EXPERT ONE ON ONE ORACLE 中的数据装载一章详细介绍了 SQLLDR 的使用方法和技巧,其中也介绍了几种装载换行符的方法。

但是今天在 ITPUB 上看到一个帖子,是关于 Tom 介绍的那个例子的,仔细看了一下, Tom 给出的例子和说明还存在一点小的问题。


首先来构建 SQLLDR 的例子,为了突出问题,将例子尽量简化:

SQL> CREATE TABLE TT (ID NUMBER, COMMENTS VARCHAR2(60));

表已创建。

控制文件的内容如下:

LOAD DATA
INFILE *
INTO TABLE TT
REPLACE
FIELDS TERMINATED BY ','
COMMENTS "REPLACE(:COMMENTS,'\n',CHR(10))"
BEGINDATA
10,This is the Sales\nOffice in Virginia
20,This is the Accounting\nOffice in Virginia
30,This is the Consulting\nOffice in Virginia
40,This is the Finance\nOffice in Virginia

使用 SQLLDR 导入数据,发现 \n 并没有被替换成换行:

SQL> HOST SQLLDR YANGTK/YANGTK CONTROL=E:\TEST.CTL

SQL> SELECT * FROM TT;

ID COMMENTS
---------- -----------------------------------------------------
10 This is the Sales\nOffice in Virginia
20 This is the Accounting\nOffice in Virginia
30 This is the Consulting\nOffice in Virginia
40 This is the Finance\nOffice in Virginia

但是,如果将 \n 都替换为 /n ,则导入结果正确:

LOAD DATA
INFILE *
INTO TABLE TT
REPLACE
FIELDS TERMINATED BY ','
COMMENTS "REPLACE(:COMMENTS,'/n',CHR(10))"
BEGINDATA
10,This is the Sales/nOffice in Virginia
20,This is the Accounting/nOffice in Virginia
30,This is the Consulting/nOffice in Virginia
40,This is the Finance/nOffice in Virginia

SQL> HOST SQLLDR YANGTK/YANGTK CONTROL=E:\TEST.CTL

SQL> SELECT * FROM TT;

ID COMMENTS
---------- --------------------------------------------------
10 This is the Sales
Office in Virginia

20 This is the Accounting
Office in Virginia

30 This is the Consulting
Office in Virginia

40 This is the Finance
Office in Virginia

而且测试发现,如果使用 \n 的话,只需要将 REPLACE 函数中的 \n 修改为 \\n 即可:

LOAD DATA
INFILE *
INTO TABLE TT
REPLACE
FIELDS TERMINATED BY ','
COMMENTS "REPLACE(:COMMENTS,'\\n',CHR(10))"
BEGINDATA
10,This is the Sales\nOffice in Virginia
20,This is the Accounting\nOffice in Virginia
30,This is the Consulting\nOffice in Virginia
40,This is the Finance\nOffice in Virginia

SQL> HOST SQLLDR YANGTK/YANGTK CONTROL=E:\TEST.CTL

SQL> SELECT * FROM TT;

ID COMMENTS
---------- --------------------------------------------
10 This is the Sales
Office in Virginia

20 This is the Accounting
Office in Virginia

30 This is the Consulting
Office in Virginia

40 This is the Finance
Office in Virginia

这是导入结果也是正确的,不过这就和 Tom 所说的有所偏差。根据 Tom 的观点,只有在 UNIX 环境下才需要使用 \\n ,而 windows 环境下 \n 是可以正常工作的。

但是通过测试却发现,无论是 windows 环境还是 UNIX 环境,都必须使用 \\n 才能得到正确的结果。 Tom 认为在 UNIX 环境下 \n 是换行标识,因此 UNIX 环境需要额外的 \ 来标识。但是我怀疑问题不是操作系统环境造成的,而是 C 语言的语法造成的。

SQLLDR Oracle 最迅速的装载工具,这个工具是用 C 语言写的恐怕不是什么值得怀疑的事情。如果这个假设成立的话,那么问题产生的原因就很明显了。在控制文件中, COMMENTS 列的格式控制部分是放在双引号中的: "REPLACE(:COMMENTS,'\n',CHR(10))" 。也就是说,这部分 SQLLDR 是当作字符串读入的。根据 C 语言的性质,字符串中的 ’\’ 字符是转义符,也就是说这个 ’\n’ C 语言解释成了换行符,因此 REPLACE 函数实际上变成了换行符替换为换行符的操作,这也是 REPLACE 操作没有生效的原因。在语言中为了处理转移符 ’\’ ,必须使用 ’\\’ 。这就是为什么 \\n 可以生效。

最后为了验证我的观点,对上面的控制文件进行简单的修改:

LOAD DATA
INFILE *
INTO TABLE TT
REPLACE
FIELDS TERMINATED BY ','
COMMENTS "REPLACE(REPLACE(:COMMENTS, ' ', CHR(10)), '\n', '!')"
BEGINDATA
10,This is the Sales\nOffice in Virginia
20,This is the Accounting\nOffice in Virginia
30,This is the Consulting\nOffice in Virginia
40,This is the Finance\nOffice in Virginia

SQL> HOST SQLLDR YANGTK/YANGTK CONTROL=E:\TEST.CTL

SQL> SELECT * FROM TT;

ID COMMENTS
---------- ------------------------------------------------------
10 This!is!the!Sales\nOffice!in!Virginia
20 This!is!the!Accounting\nOffice!in!Virginia
30 This!is!the!Consulting\nOffice!in!Virginia
40 This!is!the!Finance\nOffice!in!Virginia

果然不出所料。不过以 Tom 的功力应该是不会犯如此低级的错误的,而且我相信 Tom 也不大可能将没有测试过的例子直接写到书中。在 Oracle 的低版本测试中和我的测试有所出入亦未可知。我的测试版本是 Oracle 10.2.0.1

问题出自论坛的: http://www.itpub.net/showthread.php?s=&threadid=609330

广播电视节目制作经营许可证(京) 字第1234号 中国互联网协会会员