简体   繁体   中英

Executing an sql statement stored in a procedure parameter

I have a question about stored procedures in Oracle. Below is the stored procedure and tables as it stands:

create table STORES
(
    ID number,
    NAME varchar2(100),
    CITY varchar2(100),
    EXPIRES DATE
)

insert into stores values(1, 'Store 1', 'City 1', sysdate);
insert into stores values(2, 'Store 2', 'City 1', sysdate);
insert into stores values(3, 'Store 3', 'City 2', sysdate);

create table CLOSED
(
    ID number,
    NAME varchar2(100),
    CITY varchar2(100)
)

create or replace PROCEDURE 
pr_TestProc(subQuery  IN VARCHAR2)
IS
begin
    insert into CLOSED (ID, NAME, CITY) 
    select ID, NAME, CITY 
    from STORES 
    where ID in (1, 2, 3);
end;

What I'd like to do is replace the "in" values with the subQuery passed in as a parameter. So if I run the procedure like:

execute pr_TestProc('select ID from STORES where EXPIRES <= sysdate');

The query passed in should be executed as a subquery being run inside the procedure. Something like:

insert into CLOSED (ID, NAME, CITY) select ID, NAME, CITY 
from STORES 
where ID in (execute(subQuery));

Obviously this doesn't work, but what would be the best way to achieve this, or is it even possible?

Thanks, Brian

You can use dynamic SQL

 create or replace PROCEDURE pr_TestProc(subQuery  IN VARCHAR2)
 IS
   l_sql_stmt varchar2(1000);
 begin
   l_sql_stmt := 'insert into CLOSED (ID, NAME, CITY) ' ||
                 ' select ID, NAME, CITY ' ||
                 '   from STORES ' ||
                 '  where id in (' || subquery || ')';
   dbms_output.put_line( l_sql_stmt );
   EXECUTE IMMEDIATE l_sql_stmt;
 end;

SQL> execute pr_TestProc('select ID from STORES where EXPIRES <= sysdate');

PL/SQL procedure successfully completed.

SQL> column name format a20
SQL> column city format a20
SQL> select * from closed;

        ID NAME                 CITY
---------- -------------------- --------------------
         1 Store 1              City 1
         2 Store 2              City 1
         3 Store 3              City 2

If you are calling this procedure rarely in a system that is relatively idle, that will probably work acceptably well. If you are calling it frequently with different subqueries, however, you are going to generate a ton of non-sharable SQL statements. That will force Oracle to do a lot of hard parsing. It will also flood your shared pool with non-sharable SQL statements, likely forcing out plans that you want to be cached, forcing more hard parsing when those SQL statements are then executed again. And if you do it fast enough, you're likely to end up getting errors (or causing other processes to get errors) that Oracle couldn't allocate enough memory in the shared pool for a particular query. Plus, dynamic SQL is harder to write, harder to debug, vulnerable to SQL injection attacks, etc. so it generally makes the system harder to deal with.

A more elegant solution would be to pass in a collection rather than a subquery would be to pass in a collection

SQL> create type id_coll
  2      as table of number;
  3  /

Type created.


SQL> ed
Wrote file afiedt.buf

  1  create or replace PROCEDURE pr_TestProc( p_ids IN id_coll)
  2  is
  3  begin
  4      insert into CLOSED (ID, NAME, CITY)
  5      select ID, NAME, CITY
  6      from STORES
  7      where ID in (select column_value
  8                     from table( p_ids ) );
  9* end;
SQL> /

Procedure created.

SQL> ed
Wrote file afiedt.buf

  1  declare
  2    l_ids id_coll;
  3  begin
  4    select id
  5      bulk collect into l_ids
  6      from stores
  7     where expires <= sysdate;
  8    pr_TestProc( l_ids );
  9* end;
SQL> /

PL/SQL procedure successfully completed.

SQL> select * from closed;

        ID NAME                 CITY
---------- -------------------- --------------------
         1 Store 1              City 1
         2 Store 2              City 1
         3 Store 3              City 2

Maybe you dont need to pass the query into the stored procedure. Just call the stored procedure with the query as a parameter.

create or replace PROCEDURE 
pr_TestProc(listOfIds_IN  IN integer)
IS
begin
ids integer := listOfIds_IN;
insert into CLOSED (ID, NAME, CITY) 
select ID, NAME, CITY from STORES where ID in (ids );
end;

Call the stored procedure like this:

pr_TestProc(SELECT id FROM Table WHERE condition)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM