| Oracle Database PL/SQLユーザーズ・ガイドおよびリファレンス 10g リリース2(10.2) B19257-01 |
|
この章では、互いに関連するPL/SQLコードとデータを1つのパッケージにまとめる方法について説明します。パッケージには、APIを構成するプロシージャの集まりや、型定義と変数宣言の集まりなどが考えられます。パッケージをコンパイルしてデータベースに格納し、その内容を複数のアプリケーションで共有できます。
この章の項目は、次のとおりです。
パッケージとは、論理的に関連するPL/SQLの型、変数およびサブプログラムをグループにまとめたスキーマ・オブジェクトのことです。通常、パッケージは仕様部と本体の2つの部分で構成されますが、本体が不要な場合もあります。仕様部はパッケージへのインタフェースです。ここでは、型、変数、定数、例外、カーソル、およびパッケージの外から参照できるサブプログラムを宣言します。本体は、カーソルの問合せとサブプログラムのコードを定義します。
仕様部はインタフェース、本体はブラック ・ボックスと考えることができます。パッケージの仕様部を変更しなくても、本体をデバッグ、拡張または置換できます。
パッケージ仕様部を作成するには、SQLのCREATE PACKAGE文を使用します。CREATE PACKAGE BODY文でパッケージ本体を定義します。SQLのCREATE PACKAGE文の詳細は、『Oracle Database SQLリファレンス』を参照してください。SQLのCREATE PACKAGE BODY文の詳細は、『Oracle Database SQLリファレンス』を参照してください。
仕様部には、ストアド・プロシージャやその他のパッケージ外部のコードから見えるパブリックな宣言を入れます。サブプログラムは、仕様部で他のすべての項目の後で最後に宣言する必要があります(ただし、特定のファンクションの名前を指定するプラグマは、ファンクション仕様部の後に宣言する必要があります)。
本体には、実装の細部と、パッケージ外部のコードからは隠されているプライベートな宣言を入れます。パッケージ本体の宣言部の後には、オプションの初期化部があります。ここには、パッケージ変数を初期化する文と、その他の一度のみの設定を行う文を入れます。
AUTHID句は、すべてのパッケージ・サブプログラムがその定義者(デフォルト)と実行者のどちらの権限で実行するか、およびスキーマ・オブジェクトへの未修飾の参照が定義者と実行者のどちらのスキーマで解決されるかを決定します。詳細は、「実行者権限と定義者権限の使用(AUTHID句)」を参照してください。
コール仕様を使用すると、パッケージ・サブプログラムをJavaメソッドまたは外部Cファンクションにマップできます。コール仕様は、JavaまたはC言語の名前、パラメータ型および戻り型を対応するSQLにマップします。Javaコール仕様を作成する方法は、『Oracle Database Java開発者ガイド』を参照してください。Cコール仕様を作成する方法は、『Oracle Databaseアプリケーション開発者ガイド-基礎編』を参照してください。
Oracleデータベースに付属しているPL/SQLパッケージについては、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。
PL/SQLパッケージには、次のものが含まれています。
getメソッドとsetメソッド。これらは他のプロシージャからの直接読取りや書込みを避ける場合に使用します。
詳細は、「パッケージ宣言」を参照してください。PL/SQLパッケージの例については、例1-13「パッケージおよびパッケージ本体の作成」および例9-3を参照してください。アプリケーションから参照およびアクセスできるのは、パッケージ仕様部の宣言のみです。パッケージ本体の実装の詳細は隠ぺいされ、アクセスできません。そのため、コールする側のプログラムを再コンパイルしなくても、本体(実装)を変更できます。
ソフトウェア・エンジニアリングではパッケージに長い歴史があります。大規模なシステムのチーム開発において、パッケージは信頼性のある、メンテナンスが容易で再利用可能なコードに重要な機能を提供しています。
パッケージを使用すると、論理的に関連した型、項目およびサブプログラムを、名前付きのPL/SQLモジュールにカプセル化できます。個々のパッケージは理解しやすく、パッケージ間のインタフェースは単純かつ明快で、明確に定義されています。これはアプリケーション開発に役立ちます。
アプリケーション設計の最初の段階では、パッケージの仕様部に含まれるインタフェース情報のみが必要です。仕様部は本体がなくてもコーディングし、コンパイルできます。仕様部のコンパイルが終了すると、そのパッケージを参照するストアド・サブプログラムもコンパイルできます。アプリケーション作成の最終段階になるまで、パッケージ本体を完全に定義する必要はありません。
パッケージを使用すると、個々の型、項目およびサブプログラムについて、それがパブリック(可視でアクセス可能)なのか、またはプライベート(隠されていてアクセス不可)なのかを指定できます。たとえば、パッケージに含まれる4つのサブプログラムのうち、3つをパブリック、1つをプライベートにすることもできます。パッケージはプライベートなサブプログラムの実装を隠ぺいするため、実装が変更された場合も(アプリケーションではなく)パッケージのみが影響を受けます。このため、メンテナンスや機能拡張が簡単に実施できます。また、実装上の細部をユーザーから隠ぺいすることで、パッケージの整合性を維持できます。
パッケージ化されたパブリック変数およびカーソルは、セッションを通じて存続します。このため、同じ環境の中で実行するすべてのサブプログラムで共有できます。これによって、データベースに格納することなくトランザクション間でデータをメンテナンスできます。
パッケージ・サブプログラムを初めてコールすると、パッケージ全体がメモリーにロードされます。パッケージ内の関連するサブプログラムに対する2度目以降のコールでは、ディスクI/Oは必要ありません。
パッケージ化すると互いに依存することがなくなるため、不要な再コンパイルを避けることができます。たとえば、パッケージ・ファンクションの本体を変更した場合、Oracleはそのファンクションをコールする他のサブプログラムを再コンパイルしません。これらのサブプログラムは仕様部で宣言されたパラメータと戻り値にのみ依存するためです。再コンパイルする必要があるのは、仕様部が変更されたときのみです。
パッケージ仕様部にはパブリック宣言が入っています。宣言された項目は、パッケージ内のどの場所からでも、同じスキーマ内の別のサブプログラムからでもアクセス可能です。図9-1に有効範囲を示します。
仕様部には、アプリケーションが利用できるパッケージ・リソースのリストがあります。アプリケーションがリソースを使用するために必要な情報は、すべて仕様部の中にあります。たとえば、次の宣言は、factorialという名前のファンクションがINTEGER型の引数を1つ取り、INTEGER型の値を戻すことを示しています。
FUNCTION factorial (n INTEGER) RETURN INTEGER; -- returns n!
ファンクションのコールに必要な情報はこれのみです。ユーザーはfacの下位の実装のこと(それが反復を利用しているのか、再帰を利用しているのかなど)を考える必要がありません。
仕様部で宣言されているのが型、定数、変数、例外およびコール仕様のみであれば、パッケージ本体は不要です。下位の実装を持つのは、サブプログラムとカーソルのみです。例9-1では、パッケージは型、例外および変数を宣言していますが、サブプログラムまたはカーソルを持たないため、パッケージ本体は不要です。このようなパッケージを使用すると、セッションを通じて存続するグローバル変数(ストアド・プロシージャ、ファンクションおよびトリガーで使用できる)を定義できます。
CREATE PACKAGE trans_data AS -- bodiless package TYPE TimeRec IS RECORD ( minutes SMALLINT, hours SMALLINT); TYPE TransRec IS RECORD ( category VARCHAR2(10), account INT, amount REAL, time_of TimeRec); minimum_balance CONSTANT REAL := 10.00; number_processed INT; insufficient_funds EXCEPTION; END trans_data; /
パッケージの仕様部で宣言された型、項目、サブプログラムおよびコール仕様部を参照するときには、次のようにドット表記法を使用します。
package_name.type_name
package_name.item_name
package_name.subprogram_name
package_name.call_spec_name
パッケージ内容は、データベース・トリガー、ストアド・サブプログラム、3GLアプリケーション・プログラムおよび様々なOracleのツール製品から参照できます。たとえば、例1-14「パッケージのプロシージャのコール」や例9-3に示されているようにパッケージのプロシージャをコールできます。
次の例では、Pro*Cプログラムの無名ブロックからhire_employeeプロシージャをコールしています。実パラメータemp_id、emp_lnameおよびemp_fnameはホスト変数です。
リモート・パッケージ変数は、直接的にも間接的にも参照できません。たとえば、プロシージャがパッケージ変数を参照する場合、データベース・リンクを通じてプロシージャをコールすることはできません。
また、パッケージ内ではホスト変数を参照できません。
パッケージ本体には、パッケージ仕様部で宣言されているすべてのカーソルとサブプログラムの実装が含まれています。パッケージ本体で定義されたサブプログラムにパッケージの外側からアクセスするには、その指定がパッケージ仕様部に存在している必要があります。サブプログラムの仕様部がパッケージ仕様部に含まれていない場合、サブプログラムは同じパッケージ内の他のサブプログラムからのみコールできます。パッケージ本体は、パッケージ仕様部と同じスキーマ内にある必要があります。
サブプログラムの仕様部と本体を一致させるために、PL/SQLは、それらのヘッダーをトークンごとに比較します。このため、空白を除いて、ヘッダーは一語一語が一致している必要があります。一致していない場合は、PL/SQLによって例外が呼び出されます。例9-2を参照してください。
CREATE PACKAGE emp_bonus AS PROCEDURE calc_bonus (date_hired employees.hire_date%TYPE); END emp_bonus; / CREATE PACKAGE BODY emp_bonus AS -- the following parameter declaration raises an exception -- because 'DATE' does not match employees.hire_date%TYPE -- PROCEDURE calc_bonus (date_hired DATE) IS -- the following is correct because there is an exact match PROCEDURE calc_bonus (date_hired employees.hire_date%TYPE) IS BEGIN DBMS_OUTPUT.PUT_LINE('Employees hired on ' || date_hired || ' get bonus.'); END; END emp_bonus; /
パッケージ本体には、パッケージの内部動作に必要な型や項目を定義するプライベート宣言を入れることもできます。これらの宣言の有効範囲は、パッケージ本体に対してローカルです。このため、宣言された型と項目はパッケージ本体の中からでなければアクセスできません。パッケージ仕様部とは異なり、パッケージ本体の宣言部にはサブプログラムの本体を置くことができます。
パッケージ本体の宣言部の後には、オプションの初期化部があります。ここには、一般にパッケージの中で宣言済の変数を初期化する文がいくつか置かれています。
サブプログラムとは異なり、パッケージをコールすることもパッケージにパラメータを渡すこともできないため、パッケージの初期化部にはあまり意味がありません。このため、パッケージの初期化部は、パッケージが初めて参照されたときに一度のみ実行されます。
すでに説明したように、仕様部で宣言されているのが型、定数、変数、例外およびコール仕様部のみであればパッケージ本体は不要です。ただしその場合でも、パッケージ本体を使用して、パッケージ仕様部で宣言した項目を初期化できます。
次に示すemp_adminという名前のパッケージの例を考えます。パッケージ仕様部では、次のような型、項目およびサブプログラムを宣言します。
EmpRecTyp型
desc_salary
invalid_salary
hire_employeeおよびnth_highest_salary
fire_employeeおよびraise_salary
パッケージを作成すると、そのパッケージの型の参照、サブプログラムのコール、カーソルの使用、例外のコールなどを行うアプリケーションを開発できます。パッケージを作成すると、そのパッケージはOracleデータベースに格納され、そのパッケージの実行権限を持つアプリケーションから使用されます。
-- create the audit table to track changes CREATE TABLE emp_audit(date_of_action DATE, user_id VARCHAR2(20), package_name VARCHAR2(30)); CREATE OR REPLACE PACKAGE emp_admin AS -- Declare externally visible types, cursor, exception TYPE EmpRecTyp IS RECORD (emp_id NUMBER, sal NUMBER); CURSOR desc_salary RETURN EmpRecTyp; invalid_salary EXCEPTION; -- Declare externally callable subprograms FUNCTION hire_employee (last_name VARCHAR2, first_name VARCHAR2, email VARCHAR2, phone_number VARCHAR2, job_id VARCHAR2, salary NUMBER, commission_pct NUMBER, manager_id NUMBER, department_id NUMBER) RETURN NUMBER; PROCEDURE fire_employee (emp_id NUMBER); -- overloaded subprogram PROCEDURE fire_employee (emp_email VARCHAR2); -- overloaded subprogram PROCEDURE raise_salary (emp_id NUMBER, amount NUMBER); FUNCTION nth_highest_salary (n NUMBER) RETURN EmpRecTyp; END emp_admin; / CREATE OR REPLACE PACKAGE BODY emp_admin AS number_hired NUMBER; -- visible only in this package -- Fully define cursor specified in package CURSOR desc_salary RETURN EmpRecTyp IS SELECT employee_id, salary FROM employees ORDER BY salary DESC; -- Fully define subprograms specified in package FUNCTION hire_employee (last_name VARCHAR2, first_name VARCHAR2, email VARCHAR2, phone_number VARCHAR2, job_id VARCHAR2, salary NUMBER, commission_pct NUMBER, manager_id NUMBER, department_id NUMBER) RETURN NUMBER IS new_emp_id NUMBER; BEGIN SELECT employees_seq.NEXTVAL INTO new_emp_id FROM dual; INSERT INTO employees VALUES (new_emp_id, last_name, first_name, email, phone_number, SYSDATE, job_id, salary, commission_pct, manager_id, department_id); number_hired := number_hired + 1; DBMS_OUTPUT.PUT_LINE('The number of employees hired is ' || TO_CHAR(number_hired) ); RETURN new_emp_id; END hire_employee; PROCEDURE fire_employee (emp_id NUMBER) IS BEGIN DELETE FROM employees WHERE employee_id = emp_id; END fire_employee; PROCEDURE fire_employee (emp_email VARCHAR2) IS BEGIN DELETE FROM employees WHERE email = emp_email; END fire_employee; -- Define local function, available only inside package FUNCTION sal_ok (jobid VARCHAR2, sal NUMBER) RETURN BOOLEAN IS min_sal NUMBER; max_sal NUMBER; BEGIN SELECT MIN(salary), MAX(salary) INTO min_sal, max_sal FROM employees WHERE job_id = jobid; RETURN (sal >= min_sal) AND (sal <= max_sal); END sal_ok; PROCEDURE raise_salary (emp_id NUMBER, amount NUMBER) IS sal NUMBER(8,2); jobid VARCHAR2(10); BEGIN SELECT job_id, salary INTO jobid, sal FROM employees WHERE employee_id = emp_id; IF sal_ok(jobid, sal + amount) THEN UPDATE employees SET salary = salary + amount WHERE employee_id = emp_id; ELSE RAISE invalid_salary; END IF; EXCEPTION -- exception-handling part starts here WHEN invalid_salary THEN DBMS_OUTPUT.PUT_LINE('The salary is out of the specified range.'); END raise_salary; FUNCTION nth_highest_salary (n NUMBER) RETURN EmpRecTyp IS emp_rec EmpRecTyp; BEGIN OPEN desc_salary; FOR i IN 1..n LOOP FETCH desc_salary INTO emp_rec; END LOOP; CLOSE desc_salary; RETURN emp_rec; END nth_highest_salary; BEGIN -- initialization part starts here INSERT INTO emp_audit VALUES (SYSDATE, USER, 'EMP_ADMIN'); number_hired := 0; END emp_admin; / -- calling the package procedures DECLARE new_emp_id NUMBER(6); BEGIN new_emp_id := emp_admin.hire_employee('Belden', 'Enrique', 'EBELDEN', '555.111.2222', 'ST_CLERK', 2500, .1, 101, 110); DBMS_OUTPUT.PUT_LINE('The new employee id is ' || TO_CHAR(new_emp_id) ); EMP_ADMIN.raise_salary(new_emp_id, 100); DBMS_OUTPUT.PUT_LINE('The 10th highest salary is '|| TO_CHAR(emp_admin.nth_highest_salary(10).sal) || ', belonging to employee: ' || TO_CHAR(emp_admin.nth_highest_salary(10).emp_id) ); emp_admin.fire_employee(new_emp_id); -- you could also delete the newly added employee as follows: -- emp_admin.fire_employee('EBELDEN'); END; /
パッケージの初期化部は、パッケージが初めて参照されたときに一度のみ実行されることに注意してください。このため、上の例のINSERT文では、データベース表emp_auditに挿入される行は1行のみです。また、変数number_hiredは一度しか初期化されません。
プロシージャhire_employeeがコールされるたびに、変数number_hiredが更新されます。ただし、number_hiredが保持しているカウントは各セッションによって異なります。つまり、カウントは全ユーザーが処理した数ではなく、1人のユーザーが処理した新しい従業員の数を反映します。
PL/SQLでは、パッケージ化された複数のサブプログラムに同じ名前を付けることができます。サブプログラムで、データ型が異なるパラメータからなる類似したパラメータのセットを受け取れるようにする場合は、この方法が便利です。たとえば、例9-3のemp_adminパッケージではfire_employeeという名前の2つのプロシージャを定義しています。1番目のプロシージャは数値を受け取り、2番目のプロシージャは文字列を受け取ります。ただし、どちらのプロシージャもデータを適切に処理します。オーバーロードされたサブプログラムに適用される規則については、「サブプログラム名のオーバーロード」を参照してください。
パッケージemp_adminのパッケージ本体では、0(ゼロ)に初期化される変数number_hiredが宣言されています。本体で宣言される項目は、パッケージ内でしか使用できません。このため、パッケージの外側のPL/SQLコードからは変数number_hiredを参照できません。このような項目はプライベートと呼ばれます。
例外invalid_salaryなど、emp_adminの仕様部で宣言された項目は、パッケージの外からも見えます。このため、例外invalid_salaryはどのPL/SQLコードからも参照できます。このような項目はパブリックと呼ばれます。
セッションを通じて、または複数のトランザクションの間で維持する必要がある項目は、パッケージ本体の宣言部に置くようにしてください。たとえば、number_hiredの値はhire_employeeへの複数のコールの間も保持されています。セッションが終了すると、値が失われます。
パブリックにする必要がある項目は、パッケージ仕様部の中に置いてください。たとえば、パッケージ仕様部で宣言されたemp_recは、パブリックで使用可能です。
STANDARDという名前のパッケージではPL/SQL環境を定義しています。このパッケージの仕様部では、型、例外およびサブプログラムをグローバルに宣言します。それらは、自動的にPL/SQLプログラムで使用可能になります。たとえば、パッケージSTANDARDでは、引数の絶対値を戻すファンクションABSを次のように宣言します。
FUNCTION ABS (n NUMBER) RETURN NUMBER;
パッケージSTANDARDの内容は、アプリケーションから直接見ることができます。その内容を参照する場合もパッケージ名に接頭辞を付けて修飾名にする必要はありません。たとえば、ABSはデータベース・トリガー、ストアド・サブプログラム、Oracleのツール製品または3GLアプリケーションから次のようにコールできます。
abs_diff := ABS(x - y);
ユーザー独自のABSを宣言すると、ローカル宣言がグローバル宣言をオーバーライドします。ただし、次に示すように、完全な名前を指定して、組込みファンクションをコールできます。
abs_diff := STANDARD.ABS(x - y);
ほとんどの組込みファンクションはオーバーロードされています。たとえば、パッケージSTANDARDには次のような宣言があります。
FUNCTION TO_CHAR (right DATE) RETURN VARCHAR2;
FUNCTION TO_CHAR (left NUMBER) RETURN VARCHAR2;
FUNCTION TO_CHAR (left DATE, right VARCHAR2) RETURN VARCHAR2;
FUNCTION TO_CHAR (left NUMBER, right VARCHAR2) RETURN VARCHAR2;
PL/SQLは、仮パラメータと実パラメータの数とデータ型を比較して、どのTO_CHARのコールかを判定します。
OracleおよびOracleの様々なツール製品には、PL/SQL、SQL、Javaまたはその他のプログラミング環境からコールできるApplication Program Interface(API)を定義した製品固有のパッケージが用意されています。広く使用されているパッケージのうち、いくつかを次に示します。詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。
DBMS_ALERTパッケージでは、データベース内の特定の値が変更されたときに、データベース・トリガーを使用してアプリケーションに警告できます。その警告は、トランザクション・ベースで、非同期です(つまり、警告はタイミング・メカニズムとは無関係に作動します)。たとえば、会社ではこのパッケージを使用して、株や債券の取り引き価格が更新されたときに、投資ポートフォリオの値を更新できます。
DBMS_OUTPUTパッケージを使用すると、PL/SQLブロック、サブプログラム、パッケージおよびトリガーからの出力を表示できます。このパッケージは、PL/SQLデバッグ情報を表示する場合に特に役立ちます。PUT_LINEプロシージャは、別のトリガー、プロシージャまたはパッケージで読み取ることができるバッファに情報を出力します。この情報は、プロシージャGET_LINEをコールするか、SQL*PlusにSERVEROUTPUT ONを設定することによって表示します。例9-4に、PL/SQLブロックからの出力の表示方法を示します。
REM set server output to ON to display output from DBMS_OUTPUT SET SERVEROUTPUT ON BEGIN DBMS_OUTPUT.PUT_LINE('These are the tables that ' || USER || ' owns:'); FOR item IN (SELECT table_name FROM user_tables) LOOP DBMS_OUTPUT.PUT_LINE(item.table_name); END LOOP; END; /
パッケージDBMS_PIPEを使用すると、名前付きパイプを介して異なるセッション間で通信できます。(パイプとは、あるプロセスから他のプロセスに情報を渡すために使用するメモリーの領域のことです。)PACK_MESSAGEプロシージャとSEND_MESSAGEプロシージャを使用してパイプの中にメッセージをパックし、同じインスタンス内の別のセッションまたは待機中のアプリケーション(UNIXプログラムなど)に送信できます。
パイプのもう一端では、RECEIVE_MESSAGEプロシージャとUNPACK_MESSAGEプロシージャを使用して、メッセージを受信し、アンパック(読取り)できます。名前付きパイプは、あらゆる点で便利です。たとえば、データを収集するCプログラムを作成し、次にそれをパイプを介してOracleデータベース内のストアド・プロシージャに送信できます。
HTFパッケージおよびHTPパッケージを使用すると、PL/SQLプログラムでHTMLタグを生成できます。
パッケージUTL_FILEを使用すると、PL/SQLプログラムでオペレーティング・システム(OS)のテキスト・ファイルを読み書きできます。このパッケージは、OSの標準ストリーム・ファイルI/Oの制限されたバージョン(OPEN、PUT、GET、CLOSEの操作を含む)を提供します。
テキスト・ファイルの読み書きを実行する場合は、ファンクションFOPENをコールします。このファンクションは、それ以降のプロシージャ・コールで使用するためのファイル・ハンドルを戻します。たとえば、プロシージャPUT_LINEは、テキスト文字列と行終了文字をオープン・ファイルに書き込みます。また、プロシージャGET_LINEは、オープン・ファイルから出力バッファにテキストの行を読み込みます。
UTL_HTTPパッケージを使用すると、PL/SQLプログラムでHTTP(Hypertext Transfer Protocol)のコールアウトを実行できます。これによって、データをインターネットから取り出すことも、Oracle Web Serverカートリッジをコールすることもできます。このパッケージには2つのエントリ・ポイントがあり、各ポイントでURL(Uniform Resource Locator)を受け取り、指定されたサイトに接続し、要求されたデータを戻します。通常このデータはHTML(Hypertext Markup Language)形式のものです。
UTL_SMTPパッケージを使用すると、PL/SQLプログラムでSimple Mail Transfer Protocol(SMTP)を介して電子メールを送信できます。パッケージには、電子メール・クライアントのSMTPコマンドへのインタフェースがあり、電子メールをSMTPサーバーにディスパッチします。
パッケージを作成する場合は、別のアプリケーションで再利用できるように、汎用性を持たせるようにしてください。オラクル社が提供するパッケージをよく理解して、すでにOracleが提供している機能と重複する機能を持つパッケージを作成しないように注意してください。
パッケージ本体の前にパッケージ仕様部を設計および定義してください。仕様部に入れるのは、コール元プログラムから見える必要のあるもののみにします。こうすることで、他の開発者が実装の細部に不適切に依存しないようにできます。
コードの変更時に必要な再コンパイルを削減するために、パッケージ仕様部に置く項目はできるかぎり少なくしておきます。そうすれば、パッケージ本体を変更しても、コール元のプロシージャを再コンパイルする必要はありません。ただし、パッケージ仕様部を変更すると、Oracleはそのパッケージを参照するすべてのストアド・サブプログラムを再コンパイルする必要があります。
パッケージの中で、カーソルの仕様部を本体と切り離して別の位置に配置できます。これによって、カーソルの仕様部を変更せずに、本体のみを変更できます。カーソルの構文は、「カーソル宣言」を参照してください。
例9-5では、%ROWTYPE属性を使用して、データベース表employeesの中の行を表すレコード型を指定しています。
CREATE PACKAGE emp_stuff AS CURSOR c1 RETURN employees%ROWTYPE; -- declare cursor spec END emp_stuff; / CREATE PACKAGE BODY emp_stuff AS CURSOR c1 RETURN employees%ROWTYPE IS SELECT * FROM employees WHERE salary > 2500; -- define cursor body END emp_stuff; /
戻り値のデータ型をRETURN句で指定しているため、カーソル仕様部にはSELECT文がありません。ただしカーソル本体には、SELECT文と、カーソル仕様部と同じRETURN句が必要です。また、SELECTリスト中の項目の数とデータ型は、RETURN句と一致する必要があります。
パッケージ・カーソルを使用すると柔軟性が向上します。たとえば、上の例では、カーソル仕様部を変更することなく、カーソル本体を変更できます。
次の例に示すように、PL/SQLブロックまたはサブプログラムからパッケージ・カーソルを参照するドット表記法を使用します。
DECLARE emp_rec employees%ROWTYPE; BEGIN OPEN emp_stuff.c1; LOOP FETCH emp_stuff.c1 INTO emp_rec; -- do processing here ... EXIT WHEN emp_stuff.c1%NOTFOUND; END LOOP; CLOSE emp_stuff.c1; END; /
パッケージ・カーソルの有効範囲はPL/SQLブロックに制限されません。したがって、パッケージ・カーソルをオープンすると、クローズするか、セッションから切断するまでオープンしたままになります。
|
![]() Copyright © 2005 Oracle Corporation. All Rights Reserved. |
|