2012-01-20 64 views
3

我通過SQLLDR將一些數據加載到Oracle。源文件是「管道分隔」。SQL * Loader:處理數據中的分隔符字符

FIELDS TERMINATED BY '|' 

但是有些記錄在數據中包含管道字符而不是分隔符。所以它打破正確的記錄加載,因爲它理解indata管道字符作爲字段終止符。

你能指點我一個方向來解決這個問題嗎?

數據文件大約9 GB,因此很難手動編輯。

例如,

加載行:

ABC | 1234567 | STR 9 R 25 | 98734959,32 | 2011年12月28日

被放棄的行:

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列是最後一個。

+0

你能舉出一個有問題的例子嗎? –

+1

帶'''管道的字段是否包含在引號中?例如'DATA1 | DATA2 |「DATAWITH | PIPE」| DATA3'。在這種情況下,你可以使用'FIELDS TERMINATED BY'|' '''' –

+0

@FlorinGhita更新 – bonsvr

回答

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

這個規則表達式需要六個捕捉組(在括號內)用豎線隔開(我不得不逃避它,因爲否則它意味着在正則表達式中是OR)。除了第三個組以外的所有組都不能包含垂直條(第[^|]*),第三組可能包含任何內容(.*),並且正則表達式必須跨越行(^$)的開頭和結尾。

這樣我們確信第三組會吃掉所有多餘的分隔符。這僅適用於您只有一個可能包含分隔符的字段。如果要進行校驗,例如可以指定第四個組以數字開頭(包括在第四個加括號的塊的開始處的\d)。

我把所有的反斜槓加倍了,因爲我們在雙引號的表達式中,但是我不太確定我應該如何。

+0

這是什麼? –

+0

FILLER吸收了整行數據,正則表達式將其分解爲6個有意義的部分。如果你不知道正則表達式是什麼,請在這裏查找正則表達式標籤。 – Benoit

+0

好的,我明白了,+1 :)。我知道什麼是正則表達式,但我從來沒有使用Oracle REGEXP函數。這就像我用awk做的一樣。你用正則表達式來完成它。 –

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

OK,我建議你來解析該文件並替換的分隔符。 在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,和最後一個大塊。

你可以嘗試用 「」 分隔符SQLLDR的NEW_FILE。

更新: 該命令可以放在一個像(和命名解析。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 
+0

使用win 7.無論如何。 – bonsvr

+0

哇。無論如何,如果你有一個unix/linux手頭,它可以更快地以這種方式更正文件:) –