Разделите разделенную строку разделителем и вставьте в таблицу в oracle 11 - PullRequest
0 голосов
/ 28 октября 2018

У меня есть входная строка, разделенная разделителем, которая может иметь около 40 токенов (число может расти), я хочу вставить эти значения в таблицу, используя хранимую процедуру в Oracle 11;Что является лучшим способом сделать это

  1. создать SP с параметрами 40 IN и использовать его для вставки.
  2. Создать SP с параметром 1 IN, который будет принимать этоСтрока и разделить разделенные токены разделителя и вставить их в таблицу

Если 2-й подход выглядит хорошо, пожалуйста, предложите, как его достичь ??

Например, если строкапохоже на "abc,123,xyz,pqr,12" (здесь разделитель - запятая), поэтому после запуска SP моя таблица table1 (A varchar2, B Number, C varchar2, D varchar2, E number) должна иметь запись типа

A  | B | C | D | E
abc|123|xys|pqr |12

Я пришелЕсли вы не уверены в производительности, есть ли лучший способ сделать то же самое?

declare
  string_to_parse varchar2(2000) := 'abc,123,xyz,pqr,12';
  A varchar2(4);
  B number;
  C varchar2(4);
  D varchar2(4);
  E number;
begin

  string_to_parse := string_to_parse||',';

   A  := REGEXP_SUBSTR(string_to_parse,'[^,]+', 1, 1);
   B  := TO_NUMBER(REGEXP_SUBSTR(string_to_parse,'[^,]+', 1, 2));
   C  := REGEXP_SUBSTR(string_to_parse,'[^,]+', 1, 3);
   D  := REGEXP_SUBSTR(string_to_parse,'[^,]+', 1, 4);
   E  := TO_NUMBER(REGEXP_SUBSTR(string_to_parse,'[^,]+', 1, 5));
   dbms_output.put_line('A ' || A || ' B ' || B || ' c ' || c || ' D ' || D || ' E ' || E);
--insert into table
end;

Ответы [ 2 ]

0 голосов
/ 29 октября 2018

В данном конкретном случае разделение - это долгий путь к цели.Учитывая, что в целевой таблице может быть много столбцов (да, 5 - это слишком много для обработки каждого из них в отдельной переменной), я бы предложил использовать словарь схемы, чтобы добавить немного гибкости.

Давайтепосмотрите на процедуру, которая принимает два параметра: имя таблицы и строку, содержащую список значений, разделенных запятыми.Здесь предполагается, что в таблице есть только строковые, числовые и временные столбцы.Для реализации полной версии добавьте обработку всех необходимых типов данных в начале процедуры.

Обратите внимание, что в середине мы разбиваем строку на таблицу подстрок с использованием стандартного подхода SQL:

select level as column_id, 
       REGEXP_SUBSTR(pi_values_list, '[^,]+', 1, level) as column_val 
  from dual connect by REGEXP_SUBSTR(pi_values_list, '[^,]+', 1, level) is not null;

Вот вся процедура:

  create or replace procedure myInsertInto(pi_table_name  char,
                                           pi_values_list char)
  is
    v_statement     varchar2(30000) := 'INSERT INTO %TABLE_NAME% (%COLUMNS_LIST%) VALUES (%VALUES_LIST%)';
    v_columns_list  varchar2(10000);
    v_values_list   varchar2(10000);
  begin

    SELECT LISTAGG(T.column_name, ',') within group (order by T.column_id) ,
           LISTAGG( -- implement specific types handling here
                    CASE
                    WHEN S.column_val IS NULL
                      THEN 'NULL'
                    WHEN T.data_type = 'NUMBER'
                      THEN S.column_val
                    WHEN T.data_type IN ('DATE', 'TIMESTAMP') 
                      THEN 'TIMESTAMP ''' || S.column_val || ''''
                    WHEN T.data_type like '%CHAR%' 
                      THEN '''' || S.column_val || ''''                    
                    ELSE 'NULL'
                    END, 
           ',') within group (order by T.column_id)
    into v_columns_list,
         v_values_list
    from user_tab_cols T,
         (select level as column_id, REGEXP_SUBSTR(pi_values_list, '[^,]+', 1, level) as column_val 
            from dual connect by REGEXP_SUBSTR(pi_values_list, '[^,]+', 1, level) is not null) S
   where T.table_name = pi_table_name
     and T.column_id = S.column_id;

    if v_columns_list IS NULL then
      raise_application_error(-20000, 'Not found columns for table ' || pi_table_name);
    end if;

    -- finalizing the statement
    v_statement := replace(v_statement, '%TABLE_NAME%', pi_table_name);      
    v_statement := replace(v_statement, '%COLUMNS_LIST%', v_columns_list);      
    v_statement := replace(v_statement, '%VALUES_LIST%', v_values_list);

    execute immediate v_statement;
  end;
  /

Затем используйте это так

create table MY_TABLE (
  col_a VARCHAR2(10),
  col_b NUMBER,
  col_c VARCHAR2(10),
  col_d DATE,
  col_E VARCHAR2(10) default 'DEFAULT'
);



begin
  myInsertInto('MY_TABLE', 'abc,123,xyz,2018-01-02 23:01:10,pqr' );
  myInsertInto('MY_TABLE', 'def,345,mkr' );
  myInsertInto('MY_TABLE', 'fgh' );
end;
/
0 голосов
/ 28 октября 2018

Первый подход - нет-нет.

Второй может работать.Просто:

  1. Назначьте вашу входную строку переменной s.

Теперь в цикле:

  1. Выйдите из цикла, если длинаs равно 0
  2. Найдите первый вхождения вашего разделителя (',') с помощью instr.Присвойте его X
  3. Если X = 0, то X: = len (строка) + 1
  4. X: = X - 1
  5. Если X> 0, вставьтеsubstr (s, 1, X) в вашу таблицу
  6. Если X> 0, то s: = substr (s, X + 1, len (s))

Iне проверял его, и есть очевидные способы его оптимизации (например, вместо присвоения подстроки обратно s, вы можете сохранить «левый конец» анализируемой в данный момент детали.

Но есть лучший подход -делать в чистом sql. К сожалению, я не знаю, поддерживает ли ваша версия oracle все функции, но попробуйте этот вариант:

with 
my_input_string as (
   select 'my,delimited,,,,,,input,string' s from dual
),
string_to_rows as (
   select trim(regexp_substr(s, '[^,]+', 1, LEVEL)) col 
    from my_input_string
 connect by instr(my_input_string.s, ',', 1, LEVEL - 1) > 0
)
select *
  from string_to_rows
 where col is not null

Если это работает (и «работает», яmean - возвращает четыре строки), просто используйте это при вставке. Замените жестко закодированную строку параметром процедуры и все.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...