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;

私がやりたいのは、「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 に答える