カーソル変数

カーソル変数は、明示カーソルと似ていますが、次の点が異なります。

  • 1つの問合せに限定されません。

    カーソル変数を問合せに対してオープンし、結果セットを処理した後、カーソル変数を別の問合せのために使用できます。

  • 値を代入できます。

  • 式で使用できます。

  • サブプログラム・パラメータとして使用できます。

    カーソル変数は、サブプログラム間で問合せ結果セットを渡すために使用できます。

  • ホスト変数として使用できます。

    カーソル変数は、PL/SQLのストアド・サブプログラムとそれらのクライアントとの間で問合せ結果セットを渡すために使用できます。

  • パラメータを受け入れることはできません。

    カーソル変数にパラメータを渡すことはできませんが、問合せ全体を渡すことができます。問合せには変数を組み込むことができます。

カーソル変数がこのような柔軟性を持つ理由は、それがポインタであるため(その値が項目自体ではなく項目のアドレスを示すため)です。

カーソル変数を参照する前に、カーソル変数をオープンするか、オープンされたPL/SQLカーソル変数またはホスト・カーソル変数の値をカーソル変数に代入することで、SQL作業領域を指すようにする必要があります。

ノート:

カーソル変数と明示カーソルには互換性がありません(一方の値が期待されている場所で、もう一方は使用できません)。

ここでのトピック

カーソル変数の作成

カーソル変数を作成するには、事前定義のSYS_REFCURSOR型の変数を宣言するか、REF CURSOR型を定義してからその型の変数を宣言します。

ノート:

カーソル変数は、通称でREF CURSORと呼ばれることがあります。

REF CURSOR型の定義の基本構文は、次のとおりです。

TYPE type_name IS REF CURSOR [ RETURN return_type ]

構文およびセマンティクスの詳細は、「カーソル変数の宣言」を参照してください。

return_typeを指定する場合、REF CURSOR型とその型のカーソル変数は、強い型になりますが、指定しない場合、それらは弱い型になります。SYS_REFCURSORとその型のカーソル変数は、弱い型です。

強いカーソル変数には、指定した型を戻す問合せのみを関連付けることができます。弱いカーソル変数には、任意の問合せを関連付けることができます。

弱いカーソル変数は、強いカーソル変数よりエラーの発生する可能性が高まりますが、より柔軟な処理が可能です。弱いREF CURSOR型は、相互に互換性があり、事前定義のSYS_REFCURSOR型とも互換性があります。弱いカーソル変数の値は、他の弱いカーソル変数に代入できます。

強いカーソル変数の値を別の強いカーソル変数に代入できるのは、(戻り型が同じである以外に)両方のカーソル変数の型が同じである場合のみです。

ノート:

テーブル・ファンクションの弱いカーソル変数引数をパーティション化するには、PARTITION BY RANGEPARTITION BY HASHではなく、PARTITION BY ANY句のみを使用します。

構文およびセマンティクスの詳細は、PARALLEL_ENABLE句を参照してください。

例7-24 カーソル変数の宣言

この例では、強い型と弱い型のREF CURSOR、それらの型の変数、および事前定義のSYS_REFCURSOR型の変数を定義しています。

DECLARE
  TYPE empcurtyp IS REF CURSOR RETURN employees%ROWTYPE;  -- strong type
  TYPE genericcurtyp IS REF CURSOR;                       -- weak type

  cursor1  empcurtyp;       -- strong cursor variable
  cursor2  genericcurtyp;   -- weak cursor variable
  my_cursor SYS_REFCURSOR;  -- weak cursor variable

  TYPE deptcurtyp IS REF CURSOR RETURN departments%ROWTYPE;  -- strong type
  dept_cv deptcurtyp;  -- strong cursor variable
BEGIN
  NULL;
END;
/

例7-25 ユーザー定義の戻り型を持つカーソル変数

この例では、EmpRecTypは、ユーザー定義のRECORD型です。

DECLARE
  TYPE EmpRecTyp IS RECORD (
    employee_id NUMBER,
    last_name VARCHAR2(25),
    salary   NUMBER(8,2));

  TYPE EmpCurTyp IS REF CURSOR RETURN EmpRecTyp;
  emp_cv EmpCurTyp;
BEGIN
  NULL;
END;
/

カーソル変数のオープンおよびクローズ

カーソル変数を宣言したら、OPEN FOR文でその変数をオープンし、次の操作を実行できます。

  1. カーソル変数を問合せに関連付けます(通常、この問合せで複数の行が戻されます)。

    問合せには、バインド変数用のプレースホルダを含め、その値をOPEN FOR文のUSING句で指定できます。

  2. 問合せを処理するデータベース・リソースを割り当てます。

  3. 次のように問合せを処理します。

    1. 結果セットを識別します。

      問合せが変数を参照している場合、結果セットはそれらの値に影響を受けます。詳細は、「カーソル変数問合せの変数」を参照してください。

    2. 問合せにFOR UPDATE句がある場合、結果セットの行をロックします。

      詳細は、「SELECT FOR UPDATEカーソルとFOR UPDATEカーソル」を参照してください。

  4. カーソルを結果セットの最初の行の前に配置します。

カーソル変数は、再オープンする(別のOPEN FOR文で使用する)前にクローズする必要はありません。カーソル変数を再オープンすると、前にその変数に関連付けられていた問合せは失われます。

カーソル変数が必要なくなったら、CLOSE文を使用してクローズすることで、そのリソースを再利用できます。カーソル変数のクローズ後は、その結果セットからレコードをフェッチしたり、その属性を参照することはできません。これらの操作が試行されると、PL/SQLは事前定義の例外INVALID_CURSORを呼び出します。

クローズしたカーソル変数は、再オープンできます。

関連項目:

  • 「OPEN FOR文の構文およびセマンティクス」の詳細は、「OPEN FOR文」を参照してください

  • CLOSE文の構文およびセマンティクスの詳細は、「CLOSE文」を参照してください

カーソル変数によるデータのフェッチ

カーソル変数をオープンしたら、FETCH文を使用して、問合せ結果セットの行をフェッチできます。

カーソル変数の戻り型は、FETCH文のinto_clauseと互換性を持つ必要があります。カーソル変数が強い型の場合、PL/SQLはコンパイル時に互換性がないことを捕捉します。カーソル変数が弱い型の場合、PL/SQLは実行時に互換性がないことを捕捉し、最初のフェッチの前に事前定義の例外ROWTYPE_MISMATCHを呼び出します。

関連項目:

例7-26 カーソル変数によるデータのフェッチ

この例では、例7-6で2つの明示カーソルを使用して処理していたことを、1つのカーソル変数を使用して処理しています。最初のOPEN FOR文には、問合せ自体が含まれます。2番目のOPEN FOR文は、その値が問合せである変数を参照します。

DECLARE
  cv SYS_REFCURSOR;  -- cursor variable
 
  v_lastname  employees.last_name%TYPE;  -- variable for last_name
  v_jobid     employees.job_id%TYPE;     -- variable for job_id
 
  query_2 VARCHAR2(200) :=
    'SELECT * FROM employees
    WHERE REGEXP_LIKE (job_id, ''[ACADFIMKSA]_M[ANGR]'')
    ORDER BY job_id';
 
  v_employees employees%ROWTYPE;  -- record variable row of table
 
BEGIN
  OPEN cv FOR
    SELECT last_name, job_id FROM employees
    WHERE REGEXP_LIKE (job_id, 'S[HT]_CLERK')
    ORDER BY last_name;
 
  LOOP  -- Fetches 2 columns into variables
    FETCH cv INTO v_lastname, v_jobid;
    EXIT WHEN cv%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE( RPAD(v_lastname, 25, ' ') || v_jobid );
  END LOOP;
 
  DBMS_OUTPUT.PUT_LINE( '-------------------------------------' );
 
  OPEN cv FOR query_2;
 
  LOOP  -- Fetches entire row into the v_employees record
    FETCH cv INTO v_employees;
    EXIT WHEN cv%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE( RPAD(v_employees.last_name, 25, ' ') ||
                               v_employees.job_id );
  END LOOP;
 
  CLOSE cv;
END;
/

結果:

Atkinson                 ST_CLERK
Bell                     SH_CLERK
Bissot                   ST_CLERK
...
Walsh                    SH_CLERK
-------------------------------------
Higgins                  AC_MGR
Gruenberg                FI_MGR
Martinez                 MK_MAN
...
Errazuriz                SA_MAN

例7-27 カーソル変数からコレクションへのフェッチ

この例では、FETCH文のBULK COLLECT句を使用して、カーソル変数から2つのコレクション(ネストした表)にフェッチします。

DECLARE
  TYPE empcurtyp IS REF CURSOR;
  TYPE namelist IS TABLE OF employees.last_name%TYPE;
  TYPE sallist IS TABLE OF employees.salary%TYPE;
  emp_cv  empcurtyp;
  names   namelist;
  sals    sallist;
BEGIN
  OPEN emp_cv FOR
    SELECT last_name, salary FROM employees
    WHERE job_id = 'SA_REP'
    ORDER BY salary DESC;

  FETCH emp_cv BULK COLLECT INTO names, sals;
  CLOSE emp_cv;
  -- loop through the names and sals collections
  FOR i IN names.FIRST .. names.LAST
  LOOP
    DBMS_OUTPUT.PUT_LINE
      ('Name = ' || names(i) || ', salary = ' || sals(i));
  END LOOP;
END;
/

結果:

Name = Ozer, salary = 11500
Name = Abel, salary = 11000
Name = Vishney, salary = 10500
...
Name = Kumar, salary = 6100

カーソル変数への値の代入

PL/SQLカーソル変数に、別のPL/SQLカーソル変数またはホスト・カーソル変数の値を代入できます。

構文は次のとおりです:

target_cursor_variable := source_cursor_variable;

source_cursor_variableがオープンしている場合、代入後にtarget_cursor_variableもオープンします。2つのカーソル変数は、同じSQL作業領域を指します。

source_cursor_variableがオープンしていない場合、代入後にtarget_cursor_variableがオープンしても、source_cursor_variableはオープンしません。

カーソル変数問合せの変数

カーソル変数に関連付けられた問合せでは、有効範囲内にある任意の変数を参照できます。

OPEN FOR文を使用してカーソル変数をオープンすると、PL/SQLによって問合せ内のすべての変数が評価され、結果セットの識別時にそれらの値が使用されます。変数の値を後で変更しても、結果セットは変更されません。

結果セットを変更するには、例7-29に示すとおり、変数の値を変更してから同じ問合せでカーソル変数を再度オープンする必要があります。

例7-28 カーソル変数問合せの変数(結果セットの変更なし)

この例では、値2を持つ変数factorを参照する問合せのカーソル変数をオープンしています。したがって、sal_multipleは、各フェッチ後にfactorが増分されても、常にsalの2倍になります。

DECLARE
  sal           employees.salary%TYPE;
  sal_multiple  employees.salary%TYPE;
  factor        INTEGER := 2;
 
  cv SYS_REFCURSOR;
 
BEGIN
  OPEN cv FOR
    SELECT salary, salary*factor
    FROM employees
    WHERE job_id LIKE 'AD_%';   -- PL/SQL evaluates factor
 
  LOOP
    FETCH cv INTO sal, sal_multiple;
    EXIT WHEN cv%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE('factor = ' || factor);
    DBMS_OUTPUT.PUT_LINE('sal          = ' || sal);
    DBMS_OUTPUT.PUT_LINE('sal_multiple = ' || sal_multiple);
    factor := factor + 1;  -- Does not affect sal_multiple
  END LOOP;
 
  CLOSE cv;
END;
/

結果:

factor = 2
sal          = 4400
sal_multiple = 8800
factor = 3
sal          = 24000
sal_multiple = 48000
factor = 4
sal          = 17000
sal_multiple = 34000
factor = 5
sal          = 17000
sal_multiple = 34000

例7-29 カーソル変数問合せの変数(結果セットの変更あり)

DECLARE
  sal           employees.salary%TYPE;
  sal_multiple  employees.salary%TYPE;
  factor        INTEGER := 2;
 
  cv SYS_REFCURSOR;
 
BEGIN
  DBMS_OUTPUT.PUT_LINE('factor = ' || factor);
 
  OPEN cv FOR
    SELECT salary, salary*factor
    FROM employees
    WHERE job_id LIKE 'AD_%';   -- PL/SQL evaluates factor
 
  LOOP
    FETCH cv INTO sal, sal_multiple;
    EXIT WHEN cv%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE('sal          = ' || sal);
    DBMS_OUTPUT.PUT_LINE('sal_multiple = ' || sal_multiple);
  END LOOP;
 
  factor := factor + 1;
 
  DBMS_OUTPUT.PUT_LINE('factor = ' || factor);
 
  OPEN cv FOR
    SELECT salary, salary*factor
    FROM employees
    WHERE job_id LIKE 'AD_%';   -- PL/SQL evaluates factor
 
  LOOP
    FETCH cv INTO sal, sal_multiple;
    EXIT WHEN cv%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE('sal          = ' || sal);
    DBMS_OUTPUT.PUT_LINE('sal_multiple = ' || sal_multiple);
  END LOOP;
 
  CLOSE cv;
END;
/

結果:

factor = 2
sal          = 4400
sal_multiple = 8800
sal          = 24000
sal_multiple = 48000
sal          = 17000
sal_multiple = 34000
sal          = 17000
sal_multiple = 34000
factor = 3
sal          = 4400
sal_multiple = 13200
sal          = 24000
sal_multiple = 72000
sal          = 17000
sal_multiple = 51000
sal          = 17000
sal_multiple = 51000

コレクションの問合せ

次のすべてが該当する場合、コレクションを問い合せることができます。

  • コレクションのデータ型が、スキーマ・レベルで作成されているか、またはパッケージ仕様部で宣言されている。

  • コレクション要素のデータ型が、スカラー・データ型、ユーザー定義のデータ型またはレコード型のいずれかである。

問合せのFROM句では、コレクションはtable_collection_expressionTABLE演算子の引数として指定されます。

ノート:

SQLコンテキストでは、戻り型がパッケージ仕様部で宣言されているファンクションを使用できません。

関連項目:

例7-30 静的SQLを使用したコレクションの問合せ

この例では、カーソル変数がレコードの連想配列の問合せに関連付けられています。ネストした表型mytabは、パッケージ仕様部で宣言されています。

CREATE OR REPLACE PACKAGE pkg AUTHID DEFINER AS
  TYPE rec IS RECORD(f1 NUMBER, f2 VARCHAR2(30));
  TYPE mytab IS TABLE OF rec INDEX BY pls_integer;
END;
/

DECLARE
  v1 pkg.mytab;  -- collection of records
  v2 pkg.rec;
  c1 SYS_REFCURSOR;
BEGIN
  v1(1).f1 := 1;
  v1(1).f2 := 'one';
  OPEN c1 FOR SELECT * FROM TABLE(v1);
  FETCH c1 INTO v2;
  CLOSE c1;
  DBMS_OUTPUT.PUT_LINE('Values in record are ' || v2.f1 || ' and ' || v2.f2);
END;
/

結果:

Values in record are 1 and one

カーソル変数の属性

カーソル変数は明示カーソルと同じ属性を持ちます(「明示カーソル属性」を参照)。カーソル変数の属性値の構文は、cursor_variable_nameのすぐ後にattributeが続きます(たとえば、cv%ISOPEN)。カーソル変数をオープンしていない場合、%ISOPEN以外の属性を参照すると、事前定義の例外INVALID_CURSORが呼び出されます。

サブプログラム・パラメータとしてのカーソル変数

カーソル変数は、サブプログラム・パラメータとして使用することができ、この使用方法は、サブプログラム間で問合せ結果を渡す場合に便利です。

たとえば:

  • カーソル変数をあるサブプログラムでオープンし、別のサブプログラムで処理できます。

  • 多言語アプリケーションでは、PL/SQLサブプログラムは、カーソル変数を使用して、異なる言語で記述されたサブプログラムに結果セットを戻すことができます。

ノート:

起動元と起動先のサブプログラムは、同じデータベース・インスタンス内に存在する必要があります。データベース・リンクを介して起動されたサブプログラムに、カーソル変数を渡したり、戻すことはできません。

注意:

カーソル変数はポインタであるため、カーソル変数をサブプログラム・パラメータとして使用すると、サブプログラム・パラメータのエイリアシングの可能性が高くなり、意図しない結果が発生することがあります。詳細は、「カーソル変数パラメータによるサブプログラムのパラメータのエイリアシング」を参照してください。

カーソル変数をサブプログラムの仮パラメータとして宣言する場合、次のことに注意してください。

  • サブプログラムがカーソル変数をオープンするか、カーソル変数に値を代入する場合、パラメータ・モードはIN OUTである必要があります。

  • サブプログラムがカーソル変数からフェッチするか、カーソル変数をクローズするのみである場合、パラメータ・モードはINまたはIN OUTのどちらでも可能です。

対応するカーソル変数の仮パラメータと実パラメータの戻り型には、互換性が必要です。それ以外の場合、PL/SQLは事前定義の例外ROWTYPE_MISMATCHを呼び出します。

異なるPL/SQLユニットのサブプログラム間でカーソル変数パラメータを渡すには、パッケージでパラメータのREF CURSOR型を定義します。型がパッケージに含まれる場合、複数のサブプログラムでその型を使用できます。あるサブプログラムでその型の仮パラメータを宣言し、別のサブプログラムでその型の変数を宣言して、それらの変数を最初のサブプログラムに渡すことができます。

関連項目:

例7-31 1つの問合せ用にカーソル変数をオープンするプロシージャ

この例では、REF CURSOR型と、その型のカーソル変数パラメータをオープンするプロシージャをパッケージで定義しています。

CREATE OR REPLACE PACKAGE emp_data AUTHID DEFINER AS
  TYPE empcurtyp IS REF CURSOR RETURN employees%ROWTYPE;
  PROCEDURE open_emp_cv (emp_cv IN OUT empcurtyp);
END emp_data;
/
CREATE OR REPLACE PACKAGE BODY emp_data AS
  PROCEDURE open_emp_cv (emp_cv IN OUT EmpCurTyp) IS
  BEGIN
    OPEN emp_cv FOR SELECT * FROM employees;
  END open_emp_cv;
END emp_data;
/

例7-32 選択された問合せ用のカーソル変数のオープン(同じ戻り型)

この例では、ストアド・プロシージャが選択された問合せ用にカーソル変数パラメータをオープンします。問合せには、同じ戻り型が含まれます。

CREATE OR REPLACE PACKAGE emp_data AUTHID DEFINER AS
  TYPE empcurtyp IS REF CURSOR RETURN employees%ROWTYPE;
  PROCEDURE open_emp_cv (emp_cv IN OUT empcurtyp, choice INT);
END emp_data;
/
CREATE OR REPLACE PACKAGE BODY emp_data AS
  PROCEDURE open_emp_cv (emp_cv IN OUT empcurtyp, choice INT) IS
  BEGIN
    IF choice = 1 THEN
      OPEN emp_cv FOR SELECT *
      FROM employees
      WHERE commission_pct IS NOT NULL;
    ELSIF choice = 2 THEN
      OPEN emp_cv FOR SELECT *
      FROM employees
      WHERE salary > 2500;
    ELSIF choice = 3 THEN
      OPEN emp_cv FOR SELECT *
      FROM employees
      WHERE department_id = 100;
    END IF;
  END;
END emp_data;
/

例7-33 選択された問合せ用のカーソル変数のオープン(異なる戻り型)

この例では、ストアド・プロシージャが選択された問合せ用にカーソル変数パラメータをオープンします。問合せには、異なる戻り型が含まれます。

CREATE OR REPLACE PACKAGE admin_data AUTHID DEFINER AS
  TYPE gencurtyp IS REF CURSOR;
  PROCEDURE open_cv (generic_cv IN OUT gencurtyp, choice INT);
END admin_data;
/
CREATE OR REPLACE PACKAGE BODY admin_data AS
  PROCEDURE open_cv (generic_cv IN OUT gencurtyp, choice INT) IS
  BEGIN
    IF choice = 1 THEN
      OPEN generic_cv FOR SELECT * FROM employees;
    ELSIF choice = 2 THEN
      OPEN generic_cv FOR SELECT * FROM departments;
    ELSIF choice = 3 THEN
      OPEN generic_cv FOR SELECT * FROM jobs;
    END IF;
  END;
END admin_data;
/

ホスト変数としてのカーソル変数

カーソル変数は、ホスト変数として使用することができ、この使用方法は、PL/SQLのストアド・サブプログラムとそれらのクライアントとの間で問合せ結果を渡す場合に便利です。

カーソル変数がホスト変数の場合、PL/SQLとクライアント(ホスト環境)は、結果セットを格納するSQL作業領域に対するポインタを共有します。

カーソル変数をホスト変数として使用するには、ホスト環境内でカーソル変数を宣言し、その変数を入力ホスト変数(バインド変数)としてPL/SQLに渡します。ホスト・カーソル変数は、(弱いPL/SQLカーソル変数と同様に)すべての問合せの戻り型と互換性があります。

カーソル変数の値を1つの有効範囲から別の有効範囲に渡したとしても、SQL作業領域は、それを指すカーソル変数が存在するかぎりアクセス可能です。たとえば、例7-34で、Pro*Cプログラムはホスト・カーソル変数を埋込みPL/SQL無名ブロックに渡しています。ブロックの実行後も、カーソル変数はSQL作業領域を指しています。

クライアント側にPL/SQLエンジンがあれば、クライアントからサーバーへのコールに課される制限はありません。たとえば、クライアント側でカーソル変数を宣言し、それをサーバー側でオープンしてフェッチした後で、クライアント側で引き続きフェッチすることができます。また、複数のホスト・カーソル変数を1回の往復でオープンまたはクローズするPL/SQL無名ブロックを使用することで、ネットワークの通信量を削減できます。たとえば:

/* PL/SQL anonymous block in host environment */
BEGIN
  OPEN :emp_cv FOR SELECT * FROM employees;
  OPEN :dept_cv FOR SELECT * FROM departments;
  OPEN :loc_cv FOR SELECT * FROM locations;
END;
/

PL/SQL無名ブロックの実行後もカーソル変数はSQL作業領域を指しているため、クライアント・プログラムではそれらの作業領域を使用できます。クライアント・プログラムでカーソルが必要なくなった場合、PL/SQL無名ブロックを使用してそれらのカーソルをクローズできます。たとえば:

/* PL/SQL anonymous block in host environment */
BEGIN
  CLOSE :emp_cv;
  CLOSE :dept_cv;
  CLOSE :loc_cv;
END;
/

この方法は、Oracle Formsと同様に、マルチブロック・フォームにデータを移入する場合に便利です。たとえば、次のように1回の往復で複数のSQL作業領域をオープンできます。

/* PL/SQL anonymous block in host environment */
BEGIN
  OPEN :c1 FOR SELECT 1 FROM DUAL;
  OPEN :c2 FOR SELECT 1 FROM DUAL;
  OPEN :c3 FOR SELECT 1 FROM DUAL;
END;
/

ノート:

ホスト・カーソル変数をOracle Call Interface(OCI)クライアントからPL/SQLにバインドすると、同じサーバー・コールで変数をオープンしないかぎり、サーバー側の変数からフェッチすることはできません。

例7-34 Pro*Cクライアント・プログラムにおけるホスト変数としてのカーソル変数

この例では、Pro*Cクライアント・プログラムでカーソル変数と選択子を宣言し、それらをホスト変数としてPL/SQL無名ブロックに渡すことで、選択した問合せ用にカーソル変数をオープンしています。

EXEC SQL BEGIN DECLARE SECTION;
  SQL_CURSOR  generic_cv;  -- Declare host cursor variable.
  int         choice;      -- Declare selector.
EXEC SQL END DECLARE SECTION;
EXEC SQL ALLOCATE :generic_cv;  -- Initialize host cursor variable.
-- Pass host cursor variable and selector to PL/SQL block.
/
EXEC SQL EXECUTE
BEGIN
  IF :choice = 1 THEN
    OPEN :generic_cv FOR SELECT * FROM employees;
  ELSIF :choice = 2 THEN
    OPEN :generic_cv FOR SELECT * FROM departments;
  ELSIF :choice = 3 THEN
    OPEN :generic_cv FOR SELECT * FROM jobs;
  END IF;
END;
END-EXEC;