分割定界符分隔的字符串并插入到Oracle 11中的表中

时间:2018-10-28 14:02:13

标签: regex oracle stored-procedures plsql

我有定界符分隔的输入字符串,可以有大约40个标记(数字可能会增加),我想使用oracle 11中的存储过程将这些值插入表中; 最好的方法是什么

  
      
  1. 创建一个具有40个IN参数的SP,然后将其插入。
  2.   
  3. 使用1个IN参数创建一个SP,该参数将使用该字符串并分割定界符分隔的标记并将其插入表中
  4.   

如果第二种方法看起来不错,那么请提出如何实现它的建议?

例如,如果一个字符串像"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 :(得分:0)

在这种特殊情况下,实现目标的距离很长。 考虑到目标表可能有很多列(是的,要在一个不同的变量中处理每个列,最多需要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;
/

答案 1 :(得分:-1)

第一种方法是不行。

第二个可能有效。 简单地:

  1. 将输入字符串分配给变量s。

现在,循环中:

  1. 如果s的长度为0,则退出循环
  2. 使用instr查找分隔符(',')的首次出现。将其分配给X
  3. 如果X = 0,则X:= len(string)+1
  4. X:= X-1
  5. 如果X> 0,则将substr(s,1,X)插入表中
  6. 如果X> 0,则s:= substr(s,X + 1,len(s))

我没有对其进行测试,并且有很多种方法可以对其进行优化(例如,您可以存储当前已解析部分的“左端索引”,而不是将子字符串分配回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

如果它有效(并且按“工作”的意思,我将返回四行),只需在插入内容中使用它即可。 用过程的参数替换硬编码的字符串即可。