SQL * Loader:处理数据中的分隔符

时间:2012-01-20 10:45:33

标签: oracle sql-loader oracle11gr2

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

FIELDS TERMINATED BY '|'

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

你能指点我解决这个问题的方向吗?

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

例如,

已加载的行

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

拒绝的行:

DE4 | 2346543 | WE | 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列是最后一列。

3 个答案:

答案 0 :(得分:3)

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

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')"

这个正则表达式采用垂直条分隔的六个捕获组(括号内)(我必须将其转义,否则它在regexp中表示OR)。除第三组之外的所有组都不能包含竖线([^|]*),第三组可能包含任何内容(.*),正则表达式必须从行的开头到结尾(^$)。

通过这种方式,我们确信第三组将吃掉所有多余的分离器。这只能起作用,因为您只有一个可能包含分隔符的字段。如果要进行校对,可以指定第四组以数字开头(在第四个带括号的块的开头包括\d)。

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

答案 1 :(得分: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)但如果您有更多列或者您的第三个字段不是我认为的那样,则需要一些工作。但是,这是一个开始。

答案 2 :(得分: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和last chunk。

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

<强>更新 该命令可以放在像(并命名为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