Reputation: 1611
I have delimiter separated input string which can have around 40 tokens (the number may grow), I want to insert these value into a table using stored procedure in oracle 11; What is the best way of doing it
- create an SP with 40 IN parameters and use it to insert.
- Create an SP with 1 IN parameter which will take that string and split the delimiter separated tokens and insert them into a table
If 2nd approach looks good then please suggest how to achieve it??
For example if a string is like "abc,123,xyz,pqr,12"
(here delimiter is comma)
so after running the SP my table table1(A varchar2, B Number, C varchar2, D varchar2, E number ) should have entry like
A | B | C | D | E
abc|123|xys|pqr |12
I came up with below solution be not sure about performance , is there any better way to do the same ?
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;
Upvotes: 0
Views: 1306
Reputation: 7785
In this particular case the splitting is a long way to the goal. Considering that a target table could have a lot of columns (yes, 5 is to much to handle each of them in a distinct variable), I would suggest using schema dictionary to throw in a bit of flexibility.
Let's look at a procedure that takes two parameters: a table name and a string containing list of comma separated values. Here it's supposed that table has only string, numeric and time columns. To implement a complete version add handling of all required data types in the beginning of procedure.
Pay attention that in the middle we split a string into a table of substrings using standard SQL approach:
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;
Here is the whole procedure:
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;
/
Then use it like this
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;
/
Upvotes: 0
Reputation: 963
The first approach is a no-no.
The second may work. Simply:
Now, in loop:
I didn't test it and there are obvious ways to optimize it (for example - instead of assigning substring back to s, you could store 'left end index' of currently parsed part.
But there is better approach - to do in pure sql. Unfortunately, I don't know, whether your oracle version supports all the features, but give a try to this select:
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
If it works (and by 'works' i mean - returns four rows), just use this in your insert. Replace hardcoded string with procedure's parameter and that's it.
Upvotes: -1