执行存储在procedure参数中的sql语句

时间:2012-06-20 19:28:11

标签: sql oracle stored-procedures

我对Oracle中的存储过程有疑问。 下面是存储过程和表格:

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;

我想要做的是将“in”值替换为作为参数传入的子查询。 所以如果我运行的程序如下:

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

传入的查询应该作为在过程中运行的子查询执行。 类似的东西:

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

显然这不起作用,但实现这一目标的最佳途径是什么,或者甚至是可能的?

谢谢, 布赖恩

2 个答案:

答案 0 :(得分:1)

您可以使用动态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

如果您在相对闲置的系统中很少调用此过程,那么这可能会很好地工作。但是,如果您经常使用不同的子查询调用它,那么您将生成大量不可共享的SQL语句。这将迫使Oracle进行大量的硬解析。它还会使用不可共享的SQL语句填充您的共享池,可能会强制执行您希望缓存的计划,从而在再次执行这些SQL语句时强制进行更难的解析。如果你做得足够快,你很可能最终得到错误(或导致其他进程出错)甲骨文无法在共享池中为特定查询分配足够的内存。另外,动态SQL更难编写,更难调试,容易受到SQL注入攻击等,因此通常会使系统难以处理。

更优雅的解决方案是传递集合而不是子查询传入集合

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

答案 1 :(得分:0)

也许您不需要将查询传递给存储过程。只需使用查询作为参数调用存储过程。

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;

像这样调用存储过程:

pr_TestProc(SELECT id FROM Table WHERE condition)