Oracle - файл .CSV для нескольких таблиц одновременно - PullRequest
0 голосов
/ 27 августа 2018

Мне нужно загрузить файл .CSV в мои таблицы Oracle.но дело в том, что в одном файле CSV будут данные для нескольких таблиц.Хитрость в том, что нам нужно идентифицировать данные первого столбца для вставки в конкретную таблицу.т. е. если первый столбец имеет значение «16», то в TABLE_16 должна быть вставлена ​​вся строка, в которой будет 16 столбцов, если значение 21, в TABLE_21 будет 21 столбец и т. д.Еще одна вещь, которую нужно позаботиться, - мой CSV-файл будет содержать миллионы записей, поэтому мне нужно учитывать и производительность процесса, поэтому я думаю, что BULK COLLECT и FORALL будут лучшим подходом для быстрой вставки данных.

Когда я пытаюсь запустить следующий блок, я получаю эту ошибку:

01403. 00000 - "no data found" *Cause: No data was found from the objects. *Action: There was no data from the objects which may be due to end of fetch.


Образец .CSV Data

16,"Laura","Bissot","LBISSOT","650.124.5234",20-08-05,"ST_CLERK",3300,,121,50,"aaa",234,"asdf","ssedf","wsdrftd"
21,"Mozhe","Atkinson","MATKINSO","650.124.6234",30-10-05,"ST_CLERK",2800,,121,50,"aaa",234,"asdf","ssedf","wsdrftd","aaa",234,"asdf","ssedf","wsdrftd"
11,"James","Marlow","JAMRLOW","650.124.7234",16-02-05,"ST_CLERK",2500,,121,50
16,"TJ","Olson","TJOLSON","650.124.8234",10-04-07,"ST_CLERK",2100,,121,50,"aaa",234,"asdf","ssedf","wsdrftd"
19,"Jason","Mallin","JMALLIN","650.127.1934",14-06-04,"ST_CLERK",3300,,122,50,"aaa",234,"asdf","ssedf","wsdrftd","aaa",234,"asdf",
12,"Michael","Rogers","MROGERS","650.127.1834",26-08-06,"ST_CLERK",2900,,122,50,"aaa"
14,"Ki","Gee","KGEE","650.127.1734",12-12-07,"ST_CLERK",2400,,122,50,"aaa",234,"asdf"
30,"Ki","Gee","KGEE","650.127.1734",12-12-07,"ST_CLERK",2400,,122,50,"aaa",234,"asdf",11,"dd",23,43,789,9086,"1DRFtf","PST","RTF%$",123,"dsda",5656,"dsed",123,4333,112

create or replace type 
T_CSV_DATA as object 
(c001 varchar2(50),c002 varchar2(150),c003 varchar2(150),c004 varchar2(150),c005 varchar2(150),c006 varchar2(150),c007 varchar2(150),c008 varchar2(150),c009 varchar2(150), c010 varchar2(150), 
c011 varchar2(150),c012 varchar2(150),c013 varchar2(150),c014 varchar2(150),c015 varchar2(150),c016 varchar2(150),c017 varchar2(150),c018 varchar2(150),c019 varchar2(150), c020 varchar2(150), 
c021 varchar2(150),c022 varchar2(150),c023 varchar2(150),c024 varchar2(150),c025 varchar2(150),c026 varchar2(150),c027 varchar2(150),c028 varchar2(150),c029 varchar2(150), c030 varchar2(150));

create or replace type T_CSV_VAL as table of T_CSV_DATA;

DECLARE
 --variables to do with the copying the blob into a clob
    v_blob              BLOB;
    v_clob              CLOB;
    v_dest_offset       INTEGER := 1;
    v_src_offset        INTEGER := 1;
    v_lang_context      INTEGER := dbms_lob.default_lang_ctx;
    v_warning           INTEGER;
 --variables to do with iterating over each row of the clob
    v_new_line_pos      NUMBER;
    v_start_pos         NUMBER := 1;
    v_current_line      VARCHAR2(4000);
    v_total_len         NUMBER;
    v_curr_row          apex_application_global.vc_arr2;
    V_DATA_ASSIGN       T_CSV_VAL :=T_CSV_VAL();
    V_BULK_DATA         T_CSV_VAL :=T_CSV_VAL();
BEGIN --t_csv_line
    SELECT FILE_BLOB INTO v_blob FROM  FILE_UPLOAD  WHERE ID=7;

    dbms_lob.createtemporary(v_clob,true);
    dbms_lob.converttoclob(dest_lob => v_clob,src_blob => v_blob,amount => dbms_lob.lobmaxsize,dest_offset => v_dest_offset,src_offset
    => v_src_offset,blob_csid => dbms_lob.default_csid,lang_context => v_lang_context,warning => v_warning);

    v_total_len := dbms_lob.getlength(v_clob);
    WHILE ( v_start_pos <= v_total_len ) LOOP
        v_new_line_pos := instr(v_clob,chr(10),v_start_pos);
        IF v_new_line_pos = 0 THEN
            v_new_line_pos := v_total_len + 1;
        END IF;
        v_current_line := substr(v_clob,v_start_pos,v_new_line_pos - v_start_pos);
        v_curr_row := apex_util.string_to_table(v_current_line,',');

            V_DATA_ASSIGN.EXTEND;
            V_DATA_ASSIGN(V_DATA_ASSIGN.count) := T_CSV_DATA(v_curr_row(1),v_curr_row(2),v_curr_row(3),v_curr_row(4),v_curr_row(5),
                                                             v_curr_row(6),v_curr_row(7),v_curr_row(8),v_curr_row(9),v_curr_row(10),
                                                             v_curr_row(11),v_curr_row(12),v_curr_row(13),v_curr_row(14),v_curr_row(15),
                                                             v_curr_row(16),v_curr_row(17),v_curr_row(18),v_curr_row(19),v_curr_row(20),
                                                             v_curr_row(21),v_curr_row(22),v_curr_row(23),v_curr_row(24),v_curr_row(25),
                                                             v_curr_row(26),v_curr_row(27),v_curr_row(28),v_curr_row(29),v_curr_row(30));
            v_start_pos := v_new_line_pos + 1;
    END LOOP;

    FOR rec IN V_DATA_ASSIGN.first..V_DATA_ASSIGN.last LOOP
        IF V_DATA_ASSIGN(rec).c001 = 16 THEN -- If first value is 16, then insert into TABLE_16(will have 16 columns).
            INSERT INTO TABLE_16.....
        ELSIF  V_DATA_ASSIGN(rec).c001 = 21 THEN -- If first value is 21, then insert into TABLE_21(will have 21 columns).
            INSERT INTO TABLE_21.....
        ELSIF  V_DATA_ASSIGN(rec).c001 = 11 THEN -- If first value is 11, then insert into TABLE_11(will have 11 columns).
            INSERT INTO TABLE_11.....
        ...
            ...
        ELSIF  V_DATA_ASSIGN(rec).c001 = 30 THEN -- If first value is 30, then insert into TABLE_30(will have 30 columns). and so on...
            INSERT INTO TABLE_30.....       
        END IF;
    END LOOP;
END;

Ответы [ 3 ]

0 голосов
/ 27 августа 2018

По-моему, вы на неверном пути.PL / SQL, к которому вы склонны, не может превзойти SQL * Loader, особенно когда вы включаете прямой путь и параллельное выполнение.

Вот простой пример, который показывает как сделать это.

Создание таблиц: мне лень создавать таблицы с 16 или 21 столбцом, поэтому я использую 4(t1) и 5 ​​(t1) вместо.

SQL> create table t1 (id number, fname varchar2(20), lname varchar2(20), salary number);

Table created.

SQL> create table t2 (id number, fname varchar2(20), lname varchar2(20), salary number,hiredate date);

Table created.

Контрольный файл:

options (direct=true, parallel=true)
load data 
infile *

into table t1
  append
  when (1) = '4'
  fields terminated by ',' optionally enclosed by '"'
  trailing nullcols  
  (
  id position(1) integer external, 
  fname char,
  lname char,
  salary integer external
  )

into table t2
  append
  when (1) = '5'
  fields terminated by ',' optionally enclosed by '"'
  trailing nullcols
  (
  id position (1) integer external, 
  fname char,
  lname char,
  salary integer external,
  hiredate "to_date(:hiredate, 'dd-mm-rr')"
  )

begindata
4,"Laura","Bissot",6506,14-06-04
5,"Mozhe","Atkinson",1202,20-08-05
5,"James","Marlow",1244,30-10-05
4,"TJ","Olson",4345,16-02-05

Сеанс загрузки и результат:

SQL> $sqlldr scott/tiger control=test05.ctl log=test05.log

SQL*Loader: Release 11.2.0.2.0 - Production on Pon Kol 27 15:21:17 2018

Copyright (c) 1982, 2009, Oracle and/or its affiliates.  All rights reserved.


Load completed - logical record count 4.

SQL> select * From t1;

        ID FNAME                LNAME                    SALARY
---------- -------------------- -------------------- ----------
         4 Laura                Bissot                     6506
         4 TJ                   Olson                      4345

SQL> select * From t2;

        ID FNAME                LNAME                    SALARY HIREDATE
---------- -------------------- -------------------- ---------- ----------
         5 Mozhe                Atkinson                   1202 20/08/2005
         5 James                Marlow                     1244 30/10/2005

SQL>
0 голосов
/ 28 августа 2018

Я согласен с @Littlefoot SQL-Loader - это естественный и быстрый способ.
Альтернативой является использование Oracle «EXTERNAL TABLES».

При этом вы можете использовать оператор SELECT для файлов, находящихся в файловой системе вашей операционной системы (ОС).
Например, текстовые файлы в формате CSV.

Книга: Утилиты базы данных Раздел: Внешние таблицы https://docs.oracle.com/cd/E11882_01/server.112/e22490/part_et.htm#i436567
Функция внешних таблиц является дополнением к существующим функциям загрузчика SQL .Он позволяет получать доступ к данным во внешних источниках, как если бы они находились в таблице базы данных.
Обратите внимание, что SQL
Loader может быть лучшим выбором в ситуациях загрузки данных, которые требуют дополнительной индексации промежуточной таблицы.

Основные шаги:

1 -Создать подкаталог в файловой системе операционной системы (ОС), в которой работает экземпляр Oracle.
Дляпример: если ОС - Windows, то создайте подкаталог «IN_FILES» внутри подкаталога «DATA» на блоке «C:»

Если ОС является разновидностью Linux / Unix, то создайте подкаталог «in_files» внутри подкаталога «data»в корне.

2 -на ОС предоставляют права на чтение и запись для шага подкаталога (1) для пользователя ОС, на котором запущен экземпляр Oracle.

3 -На Oracle создайте объект каталога
Вы используете полный путь в соответствии с правилами файловой системы.
Если ОС Windows, то полный путь похож на «C:\ DATA \ IN_FILES "

Create directory external_info as 'C:\DATA\IN_FILES'

Если ОС работает под управлением Linux / Unix, тогда полный путь будет выглядеть примерно так:" / data / in_files "

Create directory external_info as '/data/in_files'

4 -В Oracle предоставьте чтение / запись для объекта каталога шага 3 в PUBLIC.

Предоставьте чтение, запись в каталог EXTERNAL_INFO для общего доступа;

5 -Создайте внешнийтаблица для доступа к CSV-файлу:

В этом примере файл имеет следующий формат:
a.CSV
b. Конец записи - CARRIAGE_RETURN, за которым следует LINE_FEED (records delimited by '\r\n')
c.Первая запись - это имена столбцов, (skip 1)
Разделитель d.Fields - запятая (fields terminated by ',')
e. Данные могут быть заключены в кавычки (ASCII-символ 34) (optionally enclosed by '"')
f. Поля с пропуском будут иметь нулевые значения (missing field values are null)
g. Имя файла «data01.csv», это имя соответствует правилам ОС
RemenberWINDOWSнечувствителен к регистру, но LINUX / Unix чувствителен к регистру.

create table data01_external
  (id       number,
   fname    varchar2(20),
   lname    varchar2(20),
   salary   number,
   hiredate date
  )
  organization external
  (type oracle_loader
   default directory external_info
   access parameters (records delimited by '\r\n'
                      badfile     'data01_%p.bad'
                      discardfile 'data01_%p.dis'
                      logfile     'data01_%p.log'
                      skip 1
                      fields terminated by ','
                             optionally enclosed by '"'
                             missing field values are null
                      (id        integer external,
                       fname     char,
                       lname     char,
                       salary    decimal external,
                       hiredate  char  date_format date mask 'dd-mm-rr'
                      )
                     )
   location ('data01.csv')
  )
  reject limit UNLIMITED;

6 -Теперь вы можете написать пакет PL / SQL для вставки данных в каждую таблицу
Для этого примера я использую таблицы @ Littlefoot T1 и T2:

Create or replace package pk_load_info
  is
    procedure pr_load(p_isbFile_name     varchar2,
                      p_onuErrCode   out number,
                      p_osbErrDesc   out varchar2
                     );
End;
/


Create or replace package body pk_load_info
is
  procedure pr_load(p_isbFile_name     varchar2,
                    p_onuErrCode   out number,
                    p_osbErrDesc    out varchar2
                   )
  is
    sbEvent    varchar2(20);
    sbSentence varchar2(200);
  Begin
    p_onuErrCode:=0;
    p_osbErrDesc:=null;
    --
    if trim(p_isbFile_name) is null then
       p_onuErrCode:=101;
       p_osbErrDesc:='The name of the file is not to be null';
       return;
    End if;
    --
    -- you can use always the same file name of maybe use different file name in every run.
    sbEvent:='alter table';
    sbSentence:='alter table data01_external default directory EXTERNAL_INFO location ('||chr(39)||trim(p_isbFile_name)||chr(39)||')';
    Dbms_Output.Put_Line('sbSentence='||sbSentence);
    execute immediate sbSentence;
    --
    -- hint "append", in direct-path INSERT, data is appended to the end of the table
    sbEvent:='insert table T1';
    insert /*+ append */ into t1
    select a.id,
           a.fname,
           a.lname,
           a.salary
    from data01_external a
    where a.id=4;
    --
    sbEvent:='insert table T2';
    insert /*+ append */ into t2
    select a.id,
           a.fname,
           a.lname,
           a.salary,
           hiredate
    from data01_external a
    where a.id=5;
  Exception
    when others then
         p_onuErrCode:=sqlcode;
         p_osbErrDesc:='Event "'||sbEvent||'" '||sqlerrm;
  End pr_load;
End pk_load_info;
/

7 -Копировать файл "data02.csv" в подкаталог шага (1)
Данные:

ID,FNAME,LNAME,SALARY,HIREDATE
4,"Laura","Bissot",6506
5,"Mozhe","Atkinson",1202,20-08-05
5,"James","Marlow",1244,30-10-05
4,"TJ","Olson",4345

8 -Тест

Declare
  nuErrcode      number;
  sbErrdesc      varchar2(2000);
  Procedure print(p_isbTexto varchar2)
  is
  Begin
    if nvl(length(p_isbTexto),0)<=255 then
       dbms_output.put_line(p_isbTexto);
    Else
       dbms_output.put_line(substr(p_isbTexto,1,254)||'¬');
       dbms_output.put_line(substr(p_isbTexto,255,255));
    End if;
  End print;
Begin
  print(to_char(systimestamp,'yyyy-mm-dd hh24:mi:ss.ff4')||'|Begin');
  dbms_application_info.set_module('SQL','Inicio');
  --
  pk_load_info.pr_load('data02.csv',
                       nuErrcode,
                       sbErrdesc
                      );
  print('nuErrcode='||nuErrcode);
  print('sbErrdesc='||sbErrdesc);
  --
  if nuErrcode=0 then
     commit;
  Else
     rollback;
  end if;
  print(to_char(systimestamp,'yyyy-mm-dd hh24:mi:ss.ff4')||'|End');
  dbms_application_info.set_action('Fin');
End;
/

9 -Просмотр данных

select *
from t1;
ID|FNAME|LNAME |SALARY|
 4|Laura|Bissot|6506  |
 4|TJ   |Olson |4345  |

select *
from t2;
ID|FNAME|LNAME   |SALARY|HIREDATE           |
5 |Mozhe|Atkinson|  1202|2005-08-20 00:00:00|
5 |James|Marlow  |  1244|2005-10-30 00:00:00|

пока

0 голосов
/ 27 августа 2018

Я думаю, что самое простое решение - сначала сбросить все данные в таблицу PRE-Staging с помощью Shell Script / SQL Loader.После того, как данные выгружены в предварительную подготовку, вы можете написать блок / пакет PLSQL для вставки определенных строк в необходимые таблицы.

В блоке PLSQL вы можете использовать массовый сбор для максимизации производительности.

Спасибо, Idrees

...