SQL * Loader: работа с символами-разделителями в данных - PullRequest
3 голосов
/ 20 января 2012

Я загружаю некоторые данные в Oracle через SQLLDR. Исходный файл имеет разделитель "труба".

FIELDS TERMINATED BY '|'

Но некоторые записи содержат символ канала в данных, а не в качестве разделителя. Таким образом, он нарушает правильную загрузку записей, поскольку понимает indata символы канала как терминатор поля.

Можете ли вы указать мне направление для решения этой проблемы?

Размер файла данных составляет около 9 ГБ, поэтому его трудно редактировать вручную.

Например,

Загруженный ряд:

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 ]

3 голосов
/ 20 января 2012

Вы не можете использовать любой разделитель и сделать что-то вроде:

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

Это регулярное выражение занимает шесть групп захвата (внутри скобок), разделенных вертикальной чертой (мне пришлось избежать этого, потому что в противном случае оно означает ИЛИ в регулярном выражении). Все группы, кроме третьей, не могут содержать вертикальную черту ([^|]*), третья группа может содержать что-либо (.*), и регулярное выражение должно охватывать от начала до конца строки (^ и $).

Таким образом, мы уверены, что третья группа съест все лишние разделители. Это работает только потому, что у вас есть только одно поле, которое может содержать разделители. Если вы хотите проверить корректуру, вы можете, например, указать, что четвертая группа начинается с цифры (включая \d в начале четвертого блока в скобках).

Я удвоил все обратные косые черты, потому что мы находимся внутри выражения в двойных кавычках, но я не совсем уверен, что должен.

2 голосов
/ 20 января 2012

ОК, я рекомендую вам разобрать файл и заменить разделитель.В командной строке в 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 с разделителем ",".

ОБНОВЛЕНИЕ: Команду можно поместить в скрипт типа (и с именем 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
2 голосов
/ 20 января 2012

Мне кажется, что на самом деле 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), но потребует немного работы, если у вас есть больше столбцов или если ваше третье поле не такое, как я думаю. Но это начало.

...