1

我对 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;

我想做的是用作为参数传入的 subQuery 替换“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));

显然这是行不通的,但是实现这一目标的最佳方法是什么,或者甚至有可能吗?

谢谢,布赖恩

4

2 回答 2

2

您可以使用动态 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 语句时强制进行更难的解析。如果你做的足够快,你很可能最终得到错误(或导致其他进程得到错误),Oracle 无法在共享池中为特定查询分配足够的内存。此外,动态 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
于 2012-06-20T20:55:05.887 回答
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)
于 2012-06-20T19:40:08.160 回答