web-dev-qa-db-ja.com

Oracleテーブル変数/配列から値を選択しますか?

私の最後の質問から続けて( Oracle PL/SQLのテーブル変数? )...

配列/テーブルに値がある場合、どのようにしてそれらを再び戻すのですか?できれば、selectステートメントなどを使用していますか?

これが私がこれまでに得たものです:

declare
    type array is table of number index by binary_integer;
    pidms array;
begin
    for i in    (
                select distinct sgbstdn_pidm
                from sgbstdn
                where sgbstdn_majr_code_1 = 'HS04'
                and sgbstdn_program_1 = 'HSCOMPH'
                )
    loop
        pidms(pidms.count+1) := i.sgbstdn_pidm;
    end loop;

    select *
    from pidms; --Oracle DOESN'T LIKE THIS BIT!!!
end;

Dbms_output.putline()を使用してそれらを出力できることはわかっていますが、他のテーブルから選択するのと同じように結果セットを取得したいと思っています。

事前にありがとう、マット

11
Sonny Boy

グローバル一時テーブルが必要になる場合があります。

Oracleでは、これらは一度作成され、呼び出されると、データはセッションに対して非公開になります。

Oracleドキュメントリンク

このようなものを試してください...

CREATE GLOBAL TEMPORARY TABLE temp_number
   ( number_column   NUMBER( 10, 0 )
   )
   ON COMMIT DELETE ROWS;

BEGIN 
   INSERT INTO temp_number
      ( number_column )
      ( select distinct sgbstdn_pidm 
          from sgbstdn 
         where sgbstdn_majr_code_1 = 'HS04' 
           and sgbstdn_program_1 = 'HSCOMPH' 
      ); 

    FOR pidms_rec IN ( SELECT number_column FROM temp_number )
    LOOP 
        -- Do something here
        NULL; 
    END LOOP; 
END; 
/
15
PaulJ

Oracleでは、PL/SQLエンジンとSQLエンジンはある程度の分離を維持しています。 PL/SQL内でSQLステートメントを実行すると、SQLステートメントはSQLエンジンに渡されます。SQLエンジンは、INDEX BYテーブルなどのPL/SQL固有の構造を認識していません。

したがって、PL/SQLブロックで型を宣言する代わりに、データベーススキーマ内に同等のコレクション型を作成する必要があります。

CREATE OR REPLACE TYPE array is table of number;
/

次に、PL/SQL内で次の2つの例のように使用できます。

SQL> l
  1  declare
  2    p  array := array();
  3  begin
  4    for i in (select level from dual connect by level < 10) loop
  5      p.extend;
  6      p(p.count) := i.level;
  7    end loop;
  8    for x in (select column_value from table(cast(p as array))) loop
  9       dbms_output.put_line(x.column_value);
 10    end loop;
 11* end;
SQL> /
1
2
3
4
5
6
7
8
9

PL/SQL procedure successfully completed.

SQL> l
  1  declare
  2    p  array := array();
  3  begin
  4    select level bulk collect into p from dual connect by level < 10;
  5    for x in (select column_value from table(cast(p as array))) loop
  6       dbms_output.put_line(x.column_value);
  7    end loop;
  8* end;
SQL> /
1
2
3
4
5
6
7
8
9

PL/SQL procedure successfully completed.

コメントに基づく追加の例

私の回答と質問自体に対するあなたのコメントに基づいて、私はこれが私がそれを実装する方法だと思います。パッケージを使用して、レコードを実際のテーブルから一度フェッチし、プライベートパッケージグローバルに格納できるようにします。開いている参照カーソルを返す関数があります。

CREATE OR REPLACE PACKAGE p_cache AS
  FUNCTION get_p_cursor RETURN sys_refcursor;
END p_cache;
/

CREATE OR REPLACE PACKAGE BODY p_cache AS

  cache_array  array;

  FUNCTION get_p_cursor RETURN sys_refcursor IS
    pCursor  sys_refcursor;
  BEGIN
    OPEN pCursor FOR SELECT * from TABLE(CAST(cache_array AS array));
    RETURN pCursor;
  END get_p_cursor;

  -- Package initialization runs once in each session that references the package
  BEGIN
    SELECT level BULK COLLECT INTO cache_array FROM dual CONNECT BY LEVEL < 10;
  END p_cache;
/
11
Dave Costa

SQL配列タイプは必要ありません。エレメントタイプがプリミティブタイプの場合は除きます。 (バーチャー、数値、日付、...)

非常に基本的なサンプル:

declare
  type TPidmList is table of sgbstdn.sgbstdn_pidm%type;
  pidms TPidmList;
begin
  select distinct sgbstdn_pidm
  bulk collect into pidms
  from sgbstdn
  where sgbstdn_majr_code_1 = 'HS04'
  and sgbstdn_program_1 = 'HSCOMPH';

  -- do something with pidms

  open :someCursor for
    select value(t) pidm
    from table(pidms) t;
end;

それを再利用したい場合、それがどのように見えるかを知るのは興味深いかもしれません。複数のコマンドを発行すると、それらがパッケージにグループ化される可能性があります。上からのプライベートパッケージ変数のトリックには欠点があります。変数をパッケージに追加すると、パッケージに状態が与えられ、ステートレスな機能の束としてではなく、奇妙な種類のシングルトンオブジェクトインスタンスとして機能します。

例えば本体を再コンパイルすると、それを以前に使用したセッションで例外が発生します。 (変数値が無効になったため)

ただし、パッケージで(またはSQLでグローバルに)型を宣言し、それを使用する必要があるメソッドでパラメーターとして使用することもできます。

create package Abc as
  type TPidmList is table of sgbstdn.sgbstdn_pidm%type;

  function CreateList(majorCode in Varchar, 
                      program in Varchar) return TPidmList;

  function Test1(list in TPidmList) return PLS_Integer;
  -- "in" to make it immutable so that PL/SQL can pass a pointer instead of a copy
  procedure Test2(list in TPidmList);
end;

create package body Abc as

  function CreateList(majorCode in Varchar, 
                      program in Varchar) return TPidmList is
    result TPidmList;
  begin
    select distinct sgbstdn_pidm
    bulk collect into result
    from sgbstdn
    where sgbstdn_majr_code_1 = majorCode
    and sgbstdn_program_1 = program;

    return result;
  end;

  function Test1(list in TPidmList) return PLS_Integer is
    result PLS_Integer := 0;
  begin
    if list is null or list.Count = 0 then
      return result;
    end if;

    for i in list.First .. list.Last loop
      if ... then
        result := result + list(i);
      end if;
    end loop;
  end;

  procedure Test2(list in TPidmList) as
  begin
    ...
  end;

  return result;
end;

それを呼び出す方法:

declare
  pidms constant Abc.TPidmList := Abc.CreateList('HS04', 'HSCOMPH');
  xyz PLS_Integer;
begin
  Abc.Test2(pidms);
  xyz := Abc.Test1(pidms);
  ...

  open :someCursor for
    select value(t) as Pidm,
           xyz as SomeValue
    from   table(pidms) t;
end;
1
Robert Giesecke