Splitting comma separated string in a PL/SQL stored proc

江枫思渺然 提交于 2019-11-26 11:02:26

问题


I\'ve CSV string 100.01,200.02,300.03 which I need to pass to a PL/SQL stored procedure in Oracle. Inside the proc,I need to insert these values in a Number column in the table.

For this, I got a working approach from over here:

How to best split csv strings in oracle 9i

[2) Using SQL\'s connect by level.].

Now,I\'ve another requirement. I need to pass 2 CSV strings[equal in length] as input to PL/SQL stored proc.And, I need to break this string and insert each value from two CSV strings into two different columns in the table.Could you please let me know how to go about it?

Example of CSV inputs: mystring varchar2(2000):=\'0.75, 0.64, 0.56, 0.45\';

myAmount varchar2(2000):= \'0.25, 0.5, 0.65, 0.8\';

myString values would go into Column A and myAmount values into Column B in the table.

Could you please let me know how to achieve this?

Thanks.


回答1:


This should do what you are looking for.. It assumes your list will always be just numbers. If that is not the case, just change the references to DBMS_SQL.NUMBER_TABLE to a table type that works for all of your data:

CREATE OR REPLACE PROCEDURE insert_from_lists(
    list1_in IN VARCHAR2,
    list2_in IN VARCHAR2,
    delimiter_in IN VARCHAR2 := ','
)
IS 
    v_tbl1 DBMS_SQL.NUMBER_TABLE;
    v_tbl2 DBMS_SQL.NUMBER_TABLE;

    FUNCTION list_to_tbl
    (
        list_in IN VARCHAR2
    )
    RETURN DBMS_SQL.NUMBER_TABLE
    IS
        v_retval DBMS_SQL.NUMBER_TABLE;
    BEGIN

        IF list_in is not null
        THEN
            /*
            || Use lengths loop through the list the correct amount of times,
            || and substr to get only the correct item for that row
            */
            FOR i in 1 .. length(list_in)-length(replace(list_in,delimiter_in,''))+1
            LOOP
                /*
                || Set the row = next item in the list
                */
                v_retval(i) := 
                        substr (
                            delimiter_in||list_in||delimiter_in,
                            instr(delimiter_in||list_in||delimiter_in, delimiter_in, 1, i  ) + 1,
                            instr (delimiter_in||list_in||delimiter_in, delimiter_in, 1, i+1) - instr (delimiter_in||list_in||delimiter_in, delimiter_in, 1, i) -1
                        );
            END LOOP;
        END IF;

        RETURN v_retval;

    END list_to_tbl;
BEGIN 
   -- Put lists into collections
   v_tbl1 := list_to_tbl(list1_in);
   v_tbl2 := list_to_tbl(list2_in);

   IF v_tbl1.COUNT <> v_tbl2.COUNT
   THEN
      raise_application_error(num => -20001, msg => 'Length of lists do not match');
   END IF;

   -- Bulk insert from collections
   FORALL i IN INDICES OF v_tbl1
      insert into tmp (a, b)
      values (v_tbl1(i), v_tbl2(i));

END insert_from_lists; 



回答2:


Here is a good solution:

FUNCTION comma_to_table(iv_raw IN VARCHAR2) RETURN dbms_utility.lname_array IS
   ltab_lname dbms_utility.lname_array;
   ln_len     BINARY_INTEGER;
BEGIN
   dbms_utility.comma_to_table(list   => iv_raw
                              ,tablen => ln_len
                              ,tab    => ltab_lname);
   FOR i IN 1 .. ln_len LOOP
      dbms_output.put_line('element ' || i || ' is ' || ltab_lname(i));
   END LOOP;
   RETURN ltab_lname;
END;

Source: CSV - comma separated values - and PL/SQL (link no longer valid)




回答3:


I use apex_util.string_to_table to parse strings, but you can use a different parser if you wish. Then you can insert the data as in this example:

declare
  myString varchar2(2000) :='0.75, 0.64, 0.56, 0.45';
  myAmount varchar2(2000) :='0.25, 0.5, 0.65, 0.8';
  v_array1 apex_application_global.vc_arr2;
  v_array2 apex_application_global.vc_arr2;
begin

  v_array1 := apex_util.string_to_table(myString, ', ');
  v_array2 := apex_util.string_to_table(myAmount, ', ');

  forall i in 1..v_array1.count
     insert into mytable (a, b) values (v_array1(i), v_array2(i));
end;

Apex_util is available from Oracle 10G onwards. Prior to this it was called htmldb_util and was not installed by default. If you can't use that you could use the string parser I wrote many years ago and posted here.




回答4:


I am not sure if this fits your oracle version. On my 10g I can use pipelined table functions:

set serveroutput on

create type number_list as table of number;

-- since you want this solution
create or replace function split_csv (i_csv varchar2) return number_list pipelined 
  is 
    mystring varchar2(2000):= i_csv;
  begin
    for r in
    ( select regexp_substr(mystring,'[^,]+',1,level) element
        from dual
     connect by level <= length(regexp_replace(mystring,'[^,]+')) + 1
    )
    loop
      --dbms_output.put_line(r.element);
      pipe row(to_number(r.element, '999999.99'));
    end loop;
  end;
/

insert into foo
select column_a,column_b from 
  (select column_value column_a, rownum rn from table(split_csv('0.75, 0.64, 0.56, 0.45'))) a 
 ,(select column_value column_b, rownum rn from table(split_csv('0.25, 0.5, 0.65, 0.8'))) b
 where a.rn = b.rn
;



回答5:


CREATE OR REPLACE PROCEDURE insert_into (
   p_errcode        OUT   NUMBER,
   p_errmesg        OUT   VARCHAR2,
   p_rowsaffected   OUT   INTEGER
)
AS
   v_param0   VARCHAR2 (30) := '0.25,2.25,33.689, abc, 99';
   v_param1   VARCHAR2 (30) := '2.65,66.32, abc-def, 21.5';
BEGIN
   FOR i IN (SELECT COLUMN_VALUE
               FROM TABLE (SPLIT (v_param0, ',')))
   LOOP
      INSERT INTO tempo
                  (col1
                  )
           VALUES (i.COLUMN_VALUE
                  );
   END LOOP;

   FOR i IN (SELECT COLUMN_VALUE
               FROM TABLE (SPLIT (v_param1, ',')))
   LOOP
      INSERT INTO tempo
                  (col2
                  )
           VALUES (i.COLUMN_VALUE
                  );
   END LOOP;
END;



回答6:


create or replace procedure pro_ss(v_str varchar2) as
v_str1 varchar2(100); 
v_comma_pos number := 0;    
v_start_pos number := 1;
begin             
    loop        
    v_comma_pos := instr(v_str,',',v_start_pos);   
    if  v_comma_pos = 0 then     
      v_str1 := substr(v_str,v_start_pos);  
      dbms_output.put_line(v_str1);    
      exit;
      end if;    
    v_str1 := substr(v_str,v_start_pos,(v_comma_pos - v_start_pos)); 
    dbms_output.put_line(v_str1);       
    v_start_pos := v_comma_pos + 1;    
    end loop; 
end;
/

call pro_ss('aa,bb,cc,dd,ee,ff,gg,hh,ii,jj');

outout: aa bb cc dd ee ff gg hh ii jj




回答7:


Many good solutions have been provided already. However, if he text is provided in a very simple comma delimited format or similar, and speed is of importance, then I have for you a solution with a TABLE function (in PL/SQL). I have also provided a rundown of some other solutions.

Please see more on the Blog Entry on Parsing a CSV into multiple columns.




回答8:


As for the connect by use case, this approach should work for you:

select regexp_substr('SMITH,ALLEN,WARD,JONES','[^,]+', 1, level)
from dual
connect by regexp_substr('SMITH,ALLEN,WARD,JONES', '[^,]+', 1, level) is not null;


来源:https://stackoverflow.com/questions/4004377/splitting-comma-separated-string-in-a-pl-sql-stored-proc

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!