我对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));
显然这不起作用,但实现这一目标的最佳途径是什么,或者甚至是可能的?
谢谢, 布赖恩
答案 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)