4

我正在通过 SQLLDR 将一些数据加载到 Oracle。源文件是“管道分隔的”。

FIELDS TERMINATED BY '|'

但是有些记录在数据中包含管道字符,而不是作为分隔符。因此,它会破坏记录的正确加载,因为它将indata管道字符理解为字段终止符。

你能给我一个解决这个问题的方向吗?

数据文件大约 9 GB,因此很难手动编辑。

例如,

加载行:

ABC|1234567|STR 9 R 25|98734959,32|28.12.2011

拒绝行:

DE4|2346543|我们| 454|956584,84|28.11.2011

错误:

Rejected - Error on table HSX, column DATE_N.
ORA-01847: day of month must be between 1 and last day of month

DATE_N 列是最后一列。

4

3 回答 3

5

您不能使用任何分隔符,并执行以下操作:

field FILLER,
col1 EXPRESSION "REGEXP_REPLACE(:field,'^([^|]*)\\|([^|]*)\\|(.*)\\|([^|]*)\\|([^|]*)\\|([^|]*)$', '\\1')",
col2 EXPRESSION "REGEXP_REPLACE(:field,'^([^|]*)\\|([^|]*)\\|(.*)\\|([^|]*)\\|([^|]*)\\|([^|]*)$', '\\2')",
col3 EXPRESSION "REGEXP_REPLACE(:field,'^([^|]*)\\|([^|]*)\\|(.*)\\|([^|]*)\\|([^|]*)\\|([^|]*)$', '\\3')",
col4 EXPRESSION "REGEXP_REPLACE(:field,'^([^|]*)\\|([^|]*)\\|(.*)\\|([^|]*)\\|([^|]*)\\|([^|]*)$', '\\4')",
col5 EXPRESSION "REGEXP_REPLACE(:field,'^([^|]*)\\|([^|]*)\\|(.*)\\|([^|]*)\\|([^|]*)\\|([^|]*)$', '\\5')",
col6 EXPRESSION "REGEXP_REPLACE(:field,'^([^|]*)\\|([^|]*)\\|(.*)\\|([^|]*)\\|([^|]*)\\|([^|]*)$', '\\6')"

这个正则表达式需要六个捕获组(在括号内),由竖线分隔(我不得不转义它,否则它在正则表达式中意味着 OR)。除了第三组之外的所有组都不能包含竖线 ( [^|]*),第三组可以包含任何内容 ( .*),并且正则表达式必须跨越行的开头到结尾 (^$)。

这样我们就可以确定第三组会吃掉所有多余的分隔符。这仅适用于您只有一个可能包含分隔符的字段。例如,如果要进行校对,可以指定第四组以数字开头(包括\d在第四个带括号的块的开头)。

我将所有反斜杠加倍,因为我们在双引号表达式中,但我不确定我应该这样做。

于 2012-01-20T15:53:56.660 回答
2

在我看来,SQL*Loader 真的不可能处理您的文件,因为第三个字段:可以包含分隔符,不被引号包围并且长度可变。相反,如果您提供的数据是一个准确的示例,那么我可以提供一个示例解决方法。首先,创建一个表,其中一列 VARCHAR2 的长度与文件中任何一行的最大长度相同。然后只需将整个文件加载到此表中。从那里您可以使用以下查询提取每一列:

with CTE as
       (select 'ABC|1234567|STR 9 R 25|98734959,32|28.12.2011' as CTETXT
          from dual
        union all
        select 'DE4|2346543|WE| 454|956584,84|28.11.2011' from dual)
select substr(CTETXT, 1, instr(CTETXT, '|') - 1) as COL1
      ,substr(CTETXT
             ,instr(CTETXT, '|', 1, 1) + 1
             ,instr(CTETXT, '|', 1, 2) - instr(CTETXT, '|', 1, 1) - 1)
         as COL2
      ,substr(CTETXT
             ,instr(CTETXT, '|', 1, 2) + 1
             ,instr(CTETXT, '|', -1, 1) - instr(CTETXT, '|', 1, 2) - 1)
         as COL3
      ,substr(CTETXT, instr(CTETXT, '|', -1, 1) + 1) as COL4
  from CTE

它并不完美(尽管它可能适用于 SQL*Loader),但如果您有更多列或者您的第三个字段不是我认为的那样,则需要做一些工作。但是,这是一个开始。

于 2012-01-20T15:14:51.573 回答
2

好的,我建议您解析文件并替换分隔符。在 Unix/linux 的命令行中,你应该这样做:

cat current_file | awk -F'|' '{printf( "%s,%s,", $1, $2); for(k=3;k<NF-2;k++) printf("%s|", $k); printf("%s,%s,%s", $(NF-2),$(NF-1),$NF);print "";}' > new_file

此命令不会更改您当前的文件。将创建一个新文件,以逗号分隔,包含五个字段。它在“|”上分割输入文件 并取第一、第二、任何东西到 antelast、antelast 和最后一块。

您可以尝试使用“,”分隔符对 new_file 进行 sqlldr。

更新: 该命令可以放在类似的脚本中(并命名为 parse.awk)

#!/usr/bin/awk
# parse.awk
BEGIN {FS="|"}
{
printf("%s,%s,", $1, $2);

for(k=3;k<NF-2;k++)
        printf("%s|", $k);

printf("%s,%s,%s\n", $(NF-2),$(NF-1),$NF);
}

你可以这样运行:

cat current_file | awk  -f parse.awk > new_file
于 2012-01-20T15:36:49.463 回答