'How to pass multiple values into one parameter in stored procedure (Oracle)
I have a parameter created for a stored procedure looking to allow user to pass in multiple inputs.
create procedure sp1 (p1 in varchar2)
as
begin
select proc_id from proc_tbl where proc_id in (p1);
end;
The user expects to input multiple values separate by comma or space such as a1, b2, c3 in p1. All the PROC_ID stored in proc_tbl are in upper case.
The sp didn't run successfully and take in the inputs as a whole string.
In normal sql in clause we can just type out like this
select proc_id from proc_tbl where proc_id in ('A1', 'B2', 'C3')
How can we apply the same logic in oracle sp without case sensitivity?
Solution 1:[1]
From the database perspective, the csv list that are giving as argument is just another string, not a list of values. So you can't use IN
.
One simple approach uses a regex function:
select proc_id from proc_tbl where regexp_like(p1, '^|,' || proc_id || ',|$', 'i');
The regex means:
proc_id
is at the beginning of the parameter string (^
) or is preceded by a comma- AND:
proc_id
is at the end of parameter string ($
) or is followed by a comma
Solution 2:[2]
From a SQL injection standpoint, you probably want to take some type of collection as a parameter. Or at least scan the input for reserve words. But after that you could just use your string as part of dynamic SQL like this:
create procedure sp1 (p1 in varchar2)
as
TYPE lt_ResultTable is TABLE OF proc_table.proc_id%TYPE;
l_tResult lt_ResultTable;
l_nResult proc_table.proc_id%type;
begin
EXECUTE IMMEDIATE 'select proc_id from proc_tbl where proc_id in ('||p1||')'
BULK COLLECT INTO l_tResult;
/*If you are guaranteed only one row will return from your query, you don't need to use a
nested table
EXECUTE IMMEDIATE 'select proc_id from proc_tbl where proc_id in ('||p1||')' INTO l_nResult;
*/
end;
Solution 3:[3]
Simple select without INTO clause would not even compile. However, there is a way to safely pass several values in one argument:
create or replace procedure sp1(p1 in varchar2) is
begin
for r in (
select xt.proc_id
from proc_tbl pt, xmltable(p1 columns proc_id varchar2(100) path '.') xt
where pt.proc_id = xt.proc_id
)
loop
dbms_output.put_line(r.proc_id);
end loop;
end;
/
Solution 4:[4]
If you have control over the parameters, it would be more robust to use a collection instead of a comma-separated list in a string. You can create your own collection or use one of the pre-built collections.
Schema
--drop table proc_tbl;
create table proc_tbl(proc_id number);
insert into proc_tbl values(1);
insert into proc_tbl values(2);
insert into proc_tbl values(3);
Procedure
--SYS.ODCINUMBERLIST is defind as a VARRAY(32767) OF NUMBER.
--Or you could create your own type, like: create type number_tab as table of number;
create or replace procedure sp1 (p1 in sys.odcinumberlist) as
v_count number;
begin
select count(*)
into v_count
from proc_tbl
where proc_id in (select column_value from table(p1));
dbms_output.put_line('Count: '||v_count);
end;
/
Sample Call
begin
sp1(sys.odcinumberlist(1,2));
end;
/
Result
------
Count: 2
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | GMB |
Solution 2 | Del |
Solution 3 | Leonid |
Solution 4 | Jon Heller |