ヘッダーをスキップ
Oracle Databaseアドバンスト・アプリケーション開発者ガイド
11gリリース1(11.1)
E05687-02
  目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

7 PL/SQLサブプログラムおよびパッケージのコード化

この章では、Oracle Databaseがアプリケーション開発用に提供するプロシージャ機能のいくつかを説明します。この章の内容は次のとおりです。


関連項目:

  • PL/SQLサブプログラムの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

  • PL/SQLパッケージの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。


PL/SQLユニットの概要

PL/SQLは、現代的なブロック構造化プログラミング言語です。この言語が持ついくつかの機能を使用すると、高性能のデータベース・アプリケーションを容易に作成できます。たとえば、PL/SQLは、ループ文や条件文など標準SQLにはないプロシージャ構造を提供します。

PL/SQLブロック内部でSQLデータ操作言語(DML)の文を直接入力できます。また、Oracleが提供するサブプログラムを使用して、データ定義言語(DDL)の文を実行できます。

PL/SQLコードはサーバー上で実行されるため、PL/SQLを使用するとデータベース・アプリケーションのかなりの部分を集中化でき、メンテナンス性およびセキュリティが強化されます。また、クライアント/サーバー・アプリケーションでは、ネットワークのオーバーヘッドも大幅に削減できます。


注意:

Oracle Formsなど、Oracleの一部のツール製品にはPL/SQLエンジンが組み込まれ、ローカルでPL/SQLを実行できます。

また、一部のデータベース・アプリケーションでは、埋込みSQLまたはOracle Call Interface(OCI)を使用する3GLプログラムのかわりにPL/SQLを使用できます。

PL/SQLユニットには、次のものが含まれます。

無名ブロック

無名ブロックとは、名前のないPL/SQLユニットのことです。無名ブロックは、オプションの宣言部分、実行可能部分および1つまたは複数のオプションの例外ハンドラで構成されます。

宣言部にはPL/SQLの変数、例外およびカーソルを宣言します。実行可能部分にはPL/SQLコードおよびSQL文を含むネストされたブロックを含めることができます。

例外ハンドラには、例外状況が発生したときに、事前定義のPL/SQL例外(NO_DATA_FOUNDまたはZERO_DIVIDE)として、またはユーザー定義の例外として起動されるコードが含まれています。

無名ブロックは、通常、SQL*Plusなどのツール製品から対話形式で使用するか、プリコンパイラ、OCIまたはSQL*Moduleアプリケーションで使用します。通常、ストアド・サブプログラムを起動するか、カーソル変数をオープンするために使用します。

例7-1の無名ブロックは、DBMS_OUTPUTパッケージを使用してHR.EMPLOYEES表の部門20のすべての従業員の名前を表示します。

例7-1 無名ブロック

SQL> DECLARE
  2    last_name  VARCHAR2(10);
  3    cursor     c1 IS SELECT LAST_NAME
  4                 FROM EMPLOYEES
  5                   WHERE DEPARTMENT_ID = 20;
  6  BEGIN
  7    OPEN c1;
  8    LOOP
  9      FETCH c1 INTO last_name;
 10      EXIT WHEN c1%NOTFOUND;
 11      DBMS_OUTPUT.PUT_LINE(last_name);
 12    END LOOP;
 13  END;
 14  /
Hartstein
Fay

PL/SQL procedure successfully completed.

SQL>

例外を使用すると、PL/SQLプログラム・ロジック内のOracle Databaseエラー条件を処理できます。これによって、使用中のアプリケーションで、クライアント・アプリケーションを異常終了させるようなエラーをサーバーが発行しないようにできます。例7-2の無名ブロックは、事前定義されたOracle Database例外NO_DATA_FOUNDを処理します(この例外が処理されない場合は、ORA-01403が発生します)。

例7-2 事前定義されたエラーの例外ハンドラを持つ無名ブロック

SQL> DECLARE
  2    Emp_number  INTEGER := 9999
  3    Emp_name    VARCHAR2(10);
  4  BEGIN
  5    SELECT LAST_NAME INTO Emp_name
  6      FROM EMPLOYEES
  7        WHERE EMPLOYEE_ID = Emp_number;
  8     DBMS_OUTPUT.PUT_LINE('Employee name is ' || Emp_name);
  9  EXCEPTION
 10     WHEN NO_DATA_FOUND THEN
 11        DBMS_OUTPUT.PUT_LINE('No such employee: ' || Emp_number);
 12  END;
 13  /
No such employee: 9999

PL/SQL procedure successfully completed.

SQL>

また、例7-3に示すように、独自の例外を定義してブロックの宣言部に宣言し、それをブロックの例外部分に指定できます。

例7-3 ユーザー定義例外の例外ハンドラを持つ無名ブロック

SQL> DECLARE
  2    Emp_name            VARCHAR2(10);
  3    Emp_number          INTEGER;
  4    Empno_out_of_range  EXCEPTION;
  5  BEGIN
  6    Emp_number := 10001;
  7    IF Emp_number > 9999 OR Emp_number < 1000 THEN
  8      RAISE Empno_out_of_range;
  9    ELSE
 10      SELECT LAST_NAME INTO Emp_name FROM EMPLOYEES
 11        WHERE EMPLOYEE_ID = Emp_number;
 12      DBMS_OUTPUT.PUT_LINE('Employee name is ' || Emp_name);
 13    END IF;
 14  EXCEPTION
 15    WHEN Empno_out_of_range THEN
 16      DBMS_OUTPUT.PUT_LINE('Employee number ' || Emp_number ||
 17        ' is out of range.');
 18  END;
 19  /
Employee number 10001 is out of range.

PL/SQL procedure successfully completed.

SQL>

関連項目:


ストアドPL/SQLユニット

ストアドPL/SQLユニットは、次のような特徴を持つサブプログラム(プロシージャまたはファンクション)またはパッケージです。

  • 固有の名前を持っています。

  • パラメータをとり、値を戻すことができます。

  • データ・ディクショナリに格納されます。

  • 多数のユーザーが起動できます。

パッケージに属しているサブプログラムをパッケージ・サブプログラム、そうでないサブプログラムをスタンドアロン・サブプログラムといいます。

内容は次のとおりです。

サブプログラムのネーミング

サブプログラムは、データベース内に格納されるため、名前を付ける必要があります。名前を付けることによって、他のストアド・サブプログラムと区別され、アプリケーションで起動できます。パブリックで参照できるスキーマ内の個々のサブプログラムは、一意の名前を持つ必要があります。その名前は、有効なPL/SQL識別子である必要があります。


注意:

SQL*Moduleによって生成されたスタブを使用してストアド・サブプログラムを起動する場合、ストアド・サブプログラム名は、起動側ホストの3GL言語(AdaやCなど)の有効な識別子である必要もあります。

サブプログラムのパラメータ

ストアド・サブプログラムには、パラメータを指定できます。例7-4のプロシージャでは、部門番号が入力パラメータになっています。入力パラメータは、パラメータ化されたカーソルc1のオープン時に使用されます。

例7-4 パラメータを使用するストアド・プロシージャ

SQL> CREATE OR REPLACE PROCEDURE get_emp_names (
  2    dept_num IN NUMBER
  3  )
  4  IS
  5    emp_name  VARCHAR2(10);
  6    CURSOR    c1 (dept_num NUMBER) IS
  7                SELECT LAST_NAME FROM EMPLOYEES
  8                  WHERE DEPARTMENT_ID = dept_num;
  9  BEGIN
 10    OPEN c1(dept_num);
 11    LOOP
 12      FETCH c1 INTO emp_name;
 13      EXIT WHEN C1%NOTFOUND;
 14      DBMS_OUTPUT.PUT_LINE(emp_name);
 15    END LOOP;
 16    CLOSE c1;
 17  END;
 18  /

Procedure created.

SQL>

サブプログラムの仮パラメータには、表7-1に示す3つの主要な属性があります。

表7-1 サブプログラム・パラメータの属性

パラメータ属性 説明

名前

名前は、有効なPL/SQL識別子である必要があります。

モード

入力のみのパラメータ(IN)、出力のみのパラメータ(OUT)、入力と出力の両方のパラメータ(IN OUT)のどれであるかを示します。モードを指定しないと、INが想定されます。

データ型

パラメータのデータ型は、標準PL/SQLデータ型です。


内容は次のとおりです。

パラメータ・モード

パラメータ・モードは、仮パラメータの動作を定義します。サブプログラムには、IN(デフォルト)、OUTIN OUTという3つのパラメータ・モードを使用できます。ファンクションには、OUTモードおよびIN OUTモードは使用しないでください。実際のプログラミングでは、ファンクションにより単一の値が戻され、かつサブプログラムに対してローカルでない変数の値が変更されないことが求められます。

表7-2に、パラメータ・モードの概要を示します。

表7-2 パラメータ・モード

IN OUT IN OUT

デフォルト

指定する必要があります。

指定する必要があります。

値をサブプログラムに渡します。

値をコール側に戻します。

初期値をサブプログラムに渡し、更新された値をコール側に戻します。

仮パラメータが定数として動作します。

仮パラメータが未初期化変数として動作します。

仮パラメータが初期化変数として動作します。

仮パラメータに値を割り当てることはできません。

仮パラメータを式の中で使用できません。値を割り当てる必要があります。

仮パラメータに値を割り当てる必要があります。

実パラメータを、定数、初期化変数、リテラルまたは式にできます。

実パラメータは変数である必要があります。

実パラメータは変数である必要があります。



関連項目:

パラメータ・モードの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

パラメータ・データ型

仮パラメータのデータ型は、次のいずれかで構成されています。

  • NUMBERVARCHAR2などの無制約の型名

  • %TYPE属性や%ROWTYPE属性を使用して制約される型


    注意:

    NUMBER(2)または VARCHAR2(20)などの数値が制約される型は、パラメータ・リストでは使用できません。

%TYPE属性および%ROWTYPE属性

型属性%TYPEおよび%ROWTYPEは、パラメータを制約するために使用します。たとえば、例7-4のプロシージャ・ヘッダーは次のように記述することができます。

PROCEDURE get_emp_names(dept_num IN EMPLOYEES.DEPARTMENT_ID%TYPE)

このようにすると、dept_numパラメータのデータ型がEMPLOYEES表のDEPARTMENT_ID列と同じになります。%TYPE(または%ROWTYPE)を使用した宣言を作成する場合は、列および表が使用可能である必要があります。

表の列の型が変更されてもアプリケーション・コードを変更する必要がないため、%TYPEの使用をお薦めします。

get_emp_namesプロシージャがパッケージの一部である場合は、前に宣言したパブリック(パッケージ)変数を使用して、パラメータのデータ型を制約できます。次に例を示します。

dept_number  NUMBER(2);
...
PROCEDURE get_emp_names(dept_num IN dept_number%TYPE);

%ROWTYPE属性は、指定された表のすべての列を含むレコードを作成するために使用します。例7-5のプロシージャは、指定された従業員IDに関するPL/SQLレコード内のEMPLOYEES表のすべての列を返します。

例7-5 %TYPEおよび%ROWTYPE属性

SQL> CREATE OR REPLACE PROCEDURE get_emp_rec (
  2    emp_number  IN  EMPLOYEES.EMPLOYEE_ID%TYPE,
  3    emp_info    OUT EMPLOYEES%ROWTYPE
  4  )
  5  IS
  6  BEGIN
  7    SELECT * INTO emp_info
  8       FROM EMPLOYEES
  9         WHERE EMPLOYEE_ID = emp_number;
 10  END;
 11  /

Procedure created.

SQL>
SQL> -- Invoke procedure from PL/SQL block:
SQL>
SQL> DECLARE
  2    emp_row  EMPLOYEES%ROWTYPE;
  3  BEGIN
  4    get_emp_rec(206, emp_row);
  5    DBMS_OUTPUT.PUT('EMPLOYEE_ID: ' || emp_row.EMPLOYEE_ID);
  6    DBMS_OUTPUT.NEW_LINE;
  7    DBMS_OUTPUT.PUT('FIRST_NAME: ' || emp_row.FIRST_NAME);
  8    DBMS_OUTPUT.NEW_LINE;
  9    DBMS_OUTPUT.PUT('LAST_NAME: ' || emp_row.LAST_NAME);
 10    DBMS_OUTPUT.NEW_LINE;
 11    DBMS_OUTPUT.PUT('EMAIL: ' || emp_row.EMAIL);
 12    DBMS_OUTPUT.NEW_LINE;
 13    DBMS_OUTPUT.PUT('PHONE_NUMBER: ' || emp_row.PHONE_NUMBER);
 14    DBMS_OUTPUT.NEW_LINE;
 15    DBMS_OUTPUT.PUT('HIRE_DATE: ' || emp_row.HIRE_DATE);
 16    DBMS_OUTPUT.NEW_LINE;
 17    DBMS_OUTPUT.PUT('JOB_ID: ' || emp_row.JOB_ID);
 18    DBMS_OUTPUT.NEW_LINE;
 19    DBMS_OUTPUT.PUT('SALARY: ' || emp_row.SALARY);
 20    DBMS_OUTPUT.NEW_LINE;
 21    DBMS_OUTPUT.PUT('COMMISSION_PCT: ' || emp_row.COMMISSION_PCT);
 22    DBMS_OUTPUT.NEW_LINE;
 23    DBMS_OUTPUT.PUT('MANAGER_ID: ' || emp_row.MANAGER_ID);
 24    DBMS_OUTPUT.NEW_LINE;
 25    DBMS_OUTPUT.PUT('DEPARTMENT_ID: ' || emp_row.DEPARTMENT_ID);
 26    DBMS_OUTPUT.NEW_LINE;
 27  END;
 28  /
EMPLOYEE_ID: 206
FIRST_NAME: William
LAST_NAME: Gietz
EMAIL: WGIETZ
PHONE_NUMBER: 415.555.0100
HIRE_DATE: 07-JUN-94
JOB_ID: AC_ACCOUNT
SALARY: 8300
COMMISSION_PCT:
MANAGER_ID: 205
DEPARTMENT_ID: 110

PL/SQL procedure successfully completed.

SQL>

ストアド・ファンクションは、%ROWTYPEを使用して宣言される値を戻すことができます。次に例を示します。

FUNCTION get_emp_rec (dept_num IN EMPLOYEES.DEPARTMENT_ID%TYPE)
   RETURN EMPLOYEES%ROWTYPE IS ...
表およびレコード

PL/SQL表をパラメータとしてストアド・サブプログラムに渡すことができます。レコードの表も、パラメータとして渡せます。


注意:

リモート・サブプログラムにPL/SQL表やレコードなどのユーザー定義型を渡す場合、タイプ・チェッカがソースを検証できるようにPL/SQLで同じ定義を使用するには、冗長なループバックDBLINKを作成してください。PL/SQLのコンパイル時に、両方のソースが同じ位置から引き出されます。

デフォルトのパラメータ値

パラメータには、デフォルト値を設定できます。パラメータにデフォルト値を設定するには、DEFAULTキーワードまたは代入演算子を使用します。たとえば、Get_emp_namesプロシージャの仕様部は次のように作成できます。

PROCEDURE Get_emp_names (Dept_num IN NUMBER DEFAULT 20) IS ...

または

PROCEDURE Get_emp_names (Dept_num IN NUMBER := 20) IS ...

パラメータにデフォルト値を使用する場合は、サブプログラムの起動時に実パラメータ・リストからそのパラメータを省略できます。起動時にパラメータ値を指定すると、デフォルト値がオーバーライドされます。


注意:

無名PL/SQLブロック内とは異なり、ストアド・サブプログラム内では、変数、カーソルおよび例外の宣言の前にキーワードDECLAREを使用しないでください。使用するとエラーが発生します。

サブプログラムの作成

サブプログラムを作成するには、テキスト・エディタを使用します。その後、SQL*Plusなどの対話形式のツール製品を使用して次の文を入力し、プロシージャを含むテキスト・ファイルをロードします。

SQL> @get_emp

プロシージャが、get_emp.sqlファイル(sqlは、デフォルトのファイル拡張子)から現行のスキーマにロードされます。コードの末尾にあるスラッシュ(/)はコードの一部ではなく、プロシージャのロードをアクティブにするためのものです。


注意:

新しいサブプログラムを作成する場合、通常はCREATE OR REPLACE PROCEDURE文またはCREATE OR REPLACE FUNCTION文を使用します。この文は、同一スキーマ内の前のバージョンのサブプログラムを新しいバージョンに置き換えます。ただし、これは警告なしで実行されます。

サブプログラム・パラメータ・リストの後にキーワードISまたはASを使用できます。


関連項目:

  • CREATE FUNCTION文の構文は、『Oracle Database SQL言語リファレンス』を参照してください。

  • CREATE PROCEDURE文の構文は、『Oracle Database SQL言語リファレンス』を参照してください。


必要な権限

サブプログラム、パッケージ仕様部またはパッケージ本体を作成するには、次の権限が必要です。

  • スキーマにサブプログラムまたはパッケージを作成するには、CREATE PROCEDUREシステム権限が必要です。他のユーザーのスキーマにサブプログラムまたはパッケージを作成するには、CREATE ANY PROCEDUREシステム権限が必要です。どちらの場合も、パッケージ本体はパッケージと同じスキーマ内に作成する必要があります。


    注意:

    エラーなしで作成する(サブプログラムまたはパッケージを正常にコンパイルする)には、さらに次の権限が必要です。
    • サブプログラムまたはパッケージの所有者は、コード本体内で参照されるすべてのオブジェクトに必要なオブジェクト権限を明示的に付与されている必要があります。

    • 所有者は、ロールを使用して必要な権限を取得することはできません。


サブプログラムまたはパッケージの所有者の権限が変更された場合、実行前にそのサブプログラムを再認証する必要があります。参照オブジェクトに必要な権限が、そのサブプログラムまたはパッケージの所有者から取り消されている場合、そのサブプログラムは実行できません。

サブプログラムのEXECUTE権限があれば、他のユーザーが所有するサブプログラムを実行できます。権限が付与されたユーザーは、そのサブプログラムの所有者のセキュリティ・ドメインでサブプログラムを実行します。このため、ユーザーは、サブプログラムが参照するオブジェクトの権限を得る必要はありません。これによって、データベース・アプリケーションおよびそのユーザーによるさらに統制のとれた効率的なセキュリティ計画が可能になります。また、すべてのサブプログラムおよびパッケージが(SYSTEM表領域内の)データ・ディクショナリに格納されます。サブプログラムおよびパッケージを作成するユーザーが使用できる領域の容量は、割当て制限によっては制御されません。


注意:

パッケージの作成にはソートが必要です。このため、パッケージを作成するユーザーは、対応付けられている一時表領域にソート・セグメントを作成できる必要があります。

サブプログラムの変更

サブプログラムを変更するには、DROP PROCEDURE文またはDROP FUNCTION文を使用してそのサブプログラムを削除した後、CREATE PROCEDURE文またはCREATE FUNCTION文を使用してそのサブプログラムを再作成する必要があります。または、CREATE OR REPLACE PROCEDURE文またはCREATE OR REPLACE FUNCTION文を使用します。この文は、サブプログラムがすでに存在する場合、最初にそれを削除してから、指定どおりに再作成します。


注意:

サブプログラムが削除される際、警告は表示されません。

サブプログラムおよびパッケージの削除

SQL文のDROP PROCEDUREDROP FUNCTIONDROP PACKAGE BODYDROP PACKAGEを使用して、スタンドアロン・サブプログラム、スタンドアロン・ファンクション、パッケージ本体またはパッケージ全体をそれぞれ削除できます。DROP PACKAGE文は、パッケージの仕様部と本体の両方を削除します。

次の文は、スキーマ内にあるOld_sal_raiseプロシージャを削除します。

DROP PROCEDURE Old_sal_raise;

必要な権限

サブプログラムまたはパッケージを削除するには、それらが自スキーマ内にあるか、またはDROP ANY PROCEDURE権限が必要です。パッケージ内の個々のサブプログラムは削除できません。これらを削除せずに、パッケージ仕様部および本体を再作成する必要があります。

外部サブプログラム

Oracle Databaseのインスタンス上で実行するPL/SQLサブプログラムは、第三世代言語(3GL)で作成された外部サブプログラムを起動できます。3GLサブプログラムは、データベースのアドレス空間とは別のアドレス空間で実行されます。


関連項目:

外部サブプログラムの詳細は、第14章「複数のプログラミング言語を使用したアプリケーションの開発」を参照してください。

PL/SQLファンクション結果キャッシュ

PL/SQLファンクション結果キャッシュを使用すると、領域と時間を大幅に節約できます。結果キャッシュPL/SQLファンクションを、異なるパラメータ値を使用して起動するたびに、それらのパラメータおよびその結果がキャッシュに格納されます。それ以降、同じパラメータ値を使用して同じファンクションを起動すると、その結果は再計算されるのではなく、キャッシュから取得されます。キャッシュは共有グローバル領域(SGA)に格納されるため、アプリケーションが実行されるすべてのセッションで使用可能です。

キャッシュ結果を計算する際に使用したデータベース・オブジェクトが更新されると、そのキャッシュ結果は無効になり、再計算が必要になります。

結果キャッシュの対象として最適のファンクションは、起動される頻度が高く、かつほとんど変更されない情報に依存するものです。

PL/SQLファンクション結果キャッシュの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

PL/SQLパッケージ

パッケージとは、データベース内に格納されている関連プログラム・オブジェクト(サブプログラム、変数、定数、カーソル、例外など)のコレクションです。

パッケージは、サブプログラムをスタンドアロンのスキーマ・オブジェクトとして作成するかわりに使用します。パッケージには、スタンドアロンのサブプログラムに比べて、多数のメリットがあります。たとえば、次のことができます。

  • アプリケーション開発をより効率的に行えます。

  • 権限をより効率的に付与できます。

  • 依存スキーマ・オブジェクトを再コンパイルせずにパッケージ・オブジェクトを変更できます。

  • Oracle Databaseで複数のパッケージ・オブジェクトを一度にメモリー内に読み込めます。

  • パッケージ内のすべてのサブプログラムが使用できるグローバル変数およびグローバル・カーソルを、そのパッケージ内に含めることができます。

  • サブプログラムをオーバーロードします。サブプログラムのオーバーロードとは、同一パッケージ内に同じ名前のサブプログラムを複数作成することです。それぞれのサブプログラムが異なる数またはデータ型の引数をとることができます。


    関連項目:

    サブプログラム名のオーバーロードの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

パッケージ仕様部は、パッケージの有効範囲外で参照できるパブリック型、変数、定数およびサブプログラムを宣言します。パッケージ本体は、パッケージ外のアプリケーションが参照できないプライベート・オブジェクトのみでなく、仕様部で宣言されているオブジェクトも定義します。

例7-6のSQL*Plusスクリプトは、1つのストアド・ファンクションおよび2つのストアド・プロシージャを含むパッケージを作成し、プロシージャの1つを起動します。

例7-6 PL/SQLパッケージの作成とパッケージ・サブプログラムの起動

SQL> -- Sequence that packaged function needs:
SQL>
SQL> CREATE SEQUENCE emp_sequence
  2    START WITH 8000
  3      INCREMENT BY 10;

Sequence created.

SQL>
SQL> -- Package specification:
SQL>
SQL> CREATE or REPLACE PACKAGE employee_management IS
  2
  3    FUNCTION hire_emp (
  4      firstname  VARCHAR2,
  5      lastname   VARCHAR2,
  6      email      VARCHAR2,
  7      phone      VARCHAR2,
  8      hiredate   DATE,
  9      job        VARCHAR2,
 10      sal        NUMBER,
 11      comm       NUMBER,
 12      mgr        NUMBER,
 13      deptno     NUMBER
 14    ) RETURN NUMBER;
 15
 16    PROCEDURE fire_emp(
 17      emp_id IN NUMBER
 18    );
 19
 20    PROCEDURE sal_raise (
 21      emp_id IN NUMBER,
 22      sal_incr IN NUMBER
 23    );
 24  END employee_management;
 25  /

Package created.

SQL> -- Package body:
SQL>
SQL> CREATE or REPLACE PACKAGE BODY employee_management IS
  2
  3    FUNCTION hire_emp (
  4      firstname  VARCHAR2,
  5      lastname   VARCHAR2,
  6      email      VARCHAR2,
  7      phone      VARCHAR2,
  8      hiredate   DATE,
  9      job        VARCHAR2,
 10      sal        NUMBER,
 11      comm       NUMBER,
 12      mgr        NUMBER,
 13      deptno     NUMBER
 14    ) RETURN NUMBER
 15    IS
 16      new_empno  NUMBER(10);
 17    BEGIN
 18      new_empno := emp_sequence.NEXTVAL;
 19
 20      INSERT INTO EMPLOYEES VALUES (
 21        new_empno,
 22        firstname,
 23        lastname,
 24        email,
 25        phone,
 26        hiredate,
 27        job,
 28        sal,
 29        comm,
 30        mgr,
 31        deptno
 32      );
 33
 34      RETURN (new_empno);
 35    END hire_emp;
 36
 37    PROCEDURE fire_emp (
 38      emp_id IN NUMBER
 39    ) IS
 40    BEGIN
 41      DELETE FROM EMPLOYEES
 42        WHERE EMPLOYEE_ID = emp_id;
 43
 44      IF SQL%NOTFOUND THEN
 45        raise_application_error(
 46          -20011,
 47          'Invalid Employee Number: ' || TO_CHAR(Emp_id)
 48        );
 49      END IF;
 50    END fire_emp;
 51
 52    PROCEDURE sal_raise (
 53      emp_id IN NUMBER,
 54      sal_incr IN NUMBER
 55    ) IS
 56    BEGIN
 57      UPDATE EMPLOYEES
 58        SET SALARY = SALARY + sal_incr
 59          WHERE EMPLOYEE_ID = emp_id;
 60
 61      IF SQL%NOTFOUND THEN
 62        raise_application_error(
 63          -20011,
 64          'Invalid Employee Number: ' || TO_CHAR(Emp_id)
 65        );
 66      END IF;
 67    END sal_raise;
 68  END employee_management;
 69  /

Package body created.

SQL>
SQL> -- Invoke packaged procedures:
SQL>
SQL> DECLARE
  2    empno  NUMBER(6);
  3    sal    NUMBER(6);
  4    temp   NUMBER(6);
  5  BEGIN
  6    empno := employee_management.hire_emp(
  7      'John',
  8      'Doe',
  9      'john.doe@example.com',
 10      '555-0100',
 11      '20-SEP-07',
 12      'ST_CLERK',
 13      2500,
 14      0,
 15      100,
 16      20);
 17
 18    DBMS_OUTPUT.PUT_LINE('New employee ID is ' || TO_CHAR(empno));
 19  END;
 20  /
New employee ID is 8000

PL/SQL procedure successfully completed.

SQL>

PL/SQLオブジェクト・サイズの制限

サブプログラム、トリガー、パッケージなどのPL/SQLストアド・データベース・オブジェクトのサイズは、共有プール内のDescriptive Intermediate Attributed Notation for Ada(DIANA)コードのサイズ(バイト単位)に制限されています。フラット化されたDIANA/codeのサイズの制限は、LinuxやUNIXでは64KBですが、デスクトップ・プラットフォームでは32KBに制限されている場合があります。

ユーザーがアクセスできるもので最も密接に関連する数値は、静的データ・ディクショナリ・ビュー*_OBJECT_SIZEPARSED_SIZEです。これには、SYS.IDL_xxx$表に格納されたDIANAのサイズがバイト単位で示されています。これは共有プールでのサイズではありません。(コンパイル中に使用される)PL/SQLコードのDIANA部分のサイズは、システム表内より共有プール内で非常に大きくなります。

パッケージの作成

パッケージの各部は、異なる文を使用して作成します。パッケージ仕様部は、CREATE PACKAGE文を使用して作成します。CREATE PACKAGE文でパブリック・パッケージ・オブジェクトを宣言します。

パッケージ本体を作成するには、CREATE PACKAGE BODY文を使用します。CREATE PACKAGE BODY文は、パッケージ仕様部で宣言されているパブリック・サブプログラムの手続き型コードを定義します。

パッケージ本体にはプライベート(またはローカル)・パッケージ・サブプログラムおよび変数も定義できます。これらのオブジェクトは、同一パッケージの本体内の他のサブプログラムでのみアクセスできます。外部ユーザーはどの権限を持っていても参照できません。

初めてアプリケーションを開発する場合、CREATE PACKAGE文またはCREATE PACKAGE BODY文にOR REPLACE句を追加すると便利な場合があります。このオプションの効果は、警告なしでパッケージまたはパッケージ本体が削除されることです。CREATE文は次のようになります。

CREATE OR REPLACE PACKAGE Package_name AS ...

および

CREATE OR REPLACE PACKAGE BODY Package_name AS ...
パッケージ・オブジェクトの作成

パッケージ本体には、次のものを含めることができます。

  • パッケージ仕様部に宣言されているサブプログラム

  • パッケージ仕様部に宣言されているカーソルの定義

  • パッケージ仕様部に宣言されていないローカル・サブプログラム

  • ローカル変数

パッケージ仕様部に宣言されているサブプログラム、カーソルおよび変数はグローバルです。これらを起動または使用できるのは、パッケージに対するEXECUTE権限を持つ外部ユーザーまたはEXECUTE ANY PROCEDURE権限を持つ外部ユーザーです。

パッケージ本体を作成する場合は、本体に定義する個々のサブプログラムが、パッケージ仕様部の宣言と同じパラメータ(名前、データ型およびモード)を持つようにする必要があります。パッケージ本体内のファンクションは、パラメータと戻り型が、名前およびデータ型について仕様部と一致する必要があります。

パッケージの作成または削除に必要な権限

パッケージ仕様部またはパッケージ本体を作成または削除するために必要な権限は、スタンドアロン・サブプログラムの作成または削除に必要な権限と同じです。「サブプログラムの作成」および「サブプログラムおよびパッケージの削除」を参照してください。

パッケージおよびパッケージ・オブジェクトのネーミング

パッケージおよびパッケージ内のすべてのパブリック・オブジェクトの名前は、所定のスキーマ内で一意である必要があります。パッケージ仕様部およびその本体は、同じ名前である必要があります。また、パッケージのメンバーの名前は、サブプログラム名の重複が必要な場合を除き、そのパッケージの有効範囲内で一意である必要があります。

パッケージの無効化とセッションの状態

パッケージ・オブジェクトを参照する各セッションは、対応するパッケージの独自のインスタンスを持っています。この中には、パブリック変数、プライベート変数、カーソルおよび定数に対する持続状態が含まれます。セッションのインスタンス化されたパッケージ(仕様部または本体)のいずれかが無効化されると、そのセッションのすべてのパッケージ・インスタンスが無効になり、再コンパイルされます。これにより、そのセッションのすべてのパッケージ・インスタンスに関するセッション状態は失われます。

セッションのパッケージが無効である場合、セッションが無効なパッケージ・インスタンスのオブジェクトを最初に使用しようとしたときに、ORA-04068が戻されます。2度目にセッションがこのようなパッケージ・コールを行うと、エラーは発生せずに、パッケージはセッションに対して再インスタンス化されます。


注意:

パフォーマンスを最適な状態に保つため、Oracle Databaseがこのエラー・メッセージを戻すのは、パッケージ状態が破棄されるたびに1回のみです。

アプリケーションでこのエラーを処理する場合は、適切な処理方法を採用するように注意してください。たとえば、あるパッケージ内のサブプログラムにより別のパッケージ内のサブプログラムが起動される場合、アプリケーションでは両方のパッケージに対するセッション状態が失われていることを認識できる必要があります。


本番環境の多くでは、パッケージが無効になるDDL操作は、通常、業務時間外に行われます。したがって、エンド・ユーザー・アプリケーションでは、このような状況は問題にならない可能性もあります。しかし、パッケージが業務時間中に無効になることがよくある場合は、パッケージ・コールが行われたときにこのエラーを処理するように、アプリケーションを作成することが必要になります。

Oracle Databaseが提供するパッケージ

データベースの機能性を拡張できるように、またはPL/SQLでSQL機能を使用できるように、Oracle Databaseには多数のパッケージが組み込まれています。これらのパッケージは、アプリケーションから起動できます。


関連項目:

これらのOracle Databaseパッケージの概要は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

バルク・バインドの概要

Oracle Databaseは2つのエンジンを使用して、PL/SQLブロックおよびサブプログラムを実行します。PL/SQLエンジンは手続き型の文を実行し、SQLエンジンはSQL文を実行します。実行中は、すべてのSQL文がこの2つのエンジン間でコンテキストをスイッチングするため、パフォーマンスが低下します。

特定のブロックまたはサブプログラムの実行に必要なコンテキストのスイッチング回数を最小化すると、パフォーマンスを大幅に改善できます。バインド変数としてコレクション要素を使用するループ内でSQL文が実行される場合、ブロックが必要とする多数のコンテキストのスイッチングによってパフォーマンスが低下することがあります。コレクションには次のものが含まれます。

  • VARRAY

  • ネストした表

  • 索引付き表

  • ホスト配列

バインドとは、SQL文内のPL/SQL変数に対して値を代入することです。バルク・バインドとは、コレクション全体を一度にバインドすることです。バルク・バインドは1つの操作でコレクション全体を2つのエンジン間で受け渡すことができます。

通常、バルク・バインドの使用によって、4つ以上のデータベース行に影響するSQL文のパフォーマンスが改善されます。SQL文によって影響される行数が多いほど、バルク・バインドによるパフォーマンスの向上率は高くなります。


注意:

この項では、PL/SQLアプリケーション内でバルク・バインドを使用するかどうかを判断するために役立つ、バルク・バインドの概要を説明します。バルク・バインドを操作する場合の例外の処理方法を含むバルク・バインドの使用方法の詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

バルク・バインドを使用する場合、パラレルDMLは無効になります。


バルク・バインドを使用する場合

次について、パフォーマンスを高めるようなパルク・バインドの使用方法を示します。

コレクションを参照するDML文

FORALLキーワードを使用するバルク・バインドによって、コレクション要素を参照するINSERTUPDATEまたはDELETE文のパフォーマンスが向上します。

例7-7のPL/SQLブロックは、管理職のID番号が7902、7698または7839の従業員について昇給を行うものですが、バルク・バインドを使用する場合と使用しない場合があります。PL/SQLがバルク・バインドを使用しないで、更新する各従業員についてSQLエンジンにSQL文を送信すると、コンテキストのスイッチングによってパフォーマンスが低下します。

例7-7 コレクションを参照するDML文

SQL> DECLARE
  2    TYPE numlist IS VARRAY (100) OF NUMBER;
  3    id NUMLIST := NUMLIST(7902, 7698, 7839);
  4  BEGIN
  5    -- Efficient method, using bulk bind:
  6
  7    FORALL i IN id.FIRST..id.LAST
  8       UPDATE EMPLOYEES
  9         SET SALARY = 1.1 * SALARY
 10           WHERE MANAGER_ID = id(i);
 11
 12    -- Slower method:
 13
 14    FOR i IN id.FIRST..id.LAST LOOP
 15      UPDATE EMPLOYEES
 16        SET SALARY = 1.1 * SALARY
 17          WHERE MANAGER_ID = id(i);
 18    END LOOP;
 19  END;
 20  /

PL/SQL procedure successfully completed.

SQL>
コレクションを参照するSELECT文

BULK COLLECT INTO句によって、コレクションを参照する問合せのパフォーマンスを改善できます。スカラー値表または%TYPE値表にBULK COLLECT INTOを使用できます。

例7-8のPL/SQLブロックは、複数の値の問合せを行ってその値をPL/SQL表に格納するものですが、バルク・バインドを使用する場合と使用しない場合があります。PL/SQLがバルク・バインドを使用しないで、選択された各従業員についてSQLエンジンにSQL文を送信すると、コンテキストのスイッチングによってパフォーマンスが低下します。

例7-8 コレクションを参照するSELECT文

SQL> DECLARE
  2    TYPE var_tab IS TABLE OF VARCHAR2(20)
  3      INDEX BY PLS_INTEGER;
  4
  5    empno    VAR_TAB;
  6    ename    VAR_TAB;
  7    counter  NUMBER;
  8
  9    CURSOR c IS
 10      SELECT EMPLOYEE_ID, LAST_NAME
 11        FROM EMPLOYEES
 12          WHERE MANAGER_ID = 7698;
 13  BEGIN
 14    -- Efficient method, using bulk bind:
 15
 16    SELECT EMPLOYEE_ID, LAST_NAME BULK COLLECT
 17      INTO empno, ename
 18        FROM EMPLOYEES
 19          WHERE MANAGER_ID = 7698;
 20
 21    -- Slower method:
 22
 23    counter := 1;
 24
 25    FOR rec IN c LOOP
 26      empno(counter) := rec.EMPLOYEE_ID;
 27      ename(counter) := rec.LAST_NAME;
 28      counter := counter + 1;
 29    END LOOP;
 30  END;
 31  /

PL/SQL procedure successfully completed.

SQL>
コレクションを参照しDMLを戻すFORループ

BULK COLLECT INTOキーワードとともにFORALLキーワードを使用すると、コレクションを参照しDMLを戻すFORループのパフォーマンスを改善できます。

例7-9のPL/SQLブロックは、従業員コレクションのボーナスを計算し、EMPLOYEES表を更新します。さらに、ボーナスをbonus_list_inst列に戻します。この操作をバルク・バインドを使用して実行し、また使用せずに実行します。PL/SQLがバルク・バインドを使用しないで、更新する各従業員についてSQLエンジンにSQL文を送信すると、コンテキストのスイッチングによってパフォーマンスが低下します。

例7-9 コレクションを参照しDMLを返すFORループ

SQL> DECLARE
  2    TYPE emp_list IS VARRAY(100) OF EMPLOYEES.EMPLOYEE_ID%TYPE;
  3    empids emp_list := emp_list(182, 187, 193, 200, 204, 206);
  4
  5    TYPE bonus_list IS TABLE OF EMPLOYEES.SALARY%TYPE;
  6    bonus_list_inst  bonus_list;
  7
  8  BEGIN
  9    -- Efficient method, using bulk bind:
 10
 11    FORALL i IN empids.FIRST..empids.LAST
 12      UPDATE EMPLOYEES
 13        SET SALARY = 0.1 * SALARY
 14          WHERE EMPLOYEE_ID = empids(i)
 15            RETURNING SALARY BULK COLLECT INTO bonus_list_inst;
 16
 17    -- Slower method:
 18
 19    FOR i IN empids.FIRST..empids.LAST LOOP
 20      UPDATE EMPLOYEES
 21        SET SALARY = 0.1 * SALARY
 22          WHERE EMPLOYEE_ID = empids(i)
 23            RETURNING SALARY INTO bonus_list_inst(i);
 24    END LOOP;
 25  END;
 26  /

PL/SQL procedure successfully completed.

SQL>

トリガー

トリガーは、特殊な種類の無名PL/SQLブロックです。文レベルで、または影響を受ける各行に対して、SQL文の前後で起動するようにトリガーを定義できます。INSTEAD OFトリガーまたはシステム・トリガー(DATABASEまたはSCHEMAに対するトリガー)も定義できます。


関連項目:

トリガーの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

ネイティブ実行のためのPL/SQLサブプログラムのコンパイル

共有ライブラリにあるネイティブ・コードにPL/SQLサブプログラムをコンパイルすると、PL/SQLサブプログラムの処理を高速化できます。

ネイティブ・コンパイルは、提供されるパッケージおよびユーザーが独自に作成したサブプログラムの両方に使用できます。この方法でコンパイルされたサブプログラムは、共有サーバー構成(従来のマルチ・スレッド・サーバー)やOracle Real Application Clusters(Oracle RAC)など、すべてのサーバー環境で動作します。

この方法では、サブプログラムから起動されたSQL文の処理は高速化されないため、SQL実行にあまり時間を必要としない計算集中型サブプログラムに使用するのが効果的です。

Javaの場合は、ncompツールを使用して独自のパッケージおよびクラスをコンパイルできます。


関連項目:

  • PL/SQLネイティブ・コンパイルの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

  • Javaネイティブ・コンパイルの詳細は、『Oracle Database Java開発者ガイド』を参照してください。


カーソル変数

カーソルは静的オブジェクトであり、カーソル変数はカーソルへのポインタです。そのため、サブプログラムとの間でパラメータとして受け渡すことができます。カーソル変数は、その存続期間内に別のカーソルを参照することもできます。

カーソル変数には、その他にも次のような利点があります。


関連項目:

カーソル変数の詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

内容は次のとおりです。

カーソル変数の宣言およびオープン

メモリーは、通常、適切なALLOCATE文を使用してクライアント・アプリケーションのカーソル変数に割り当てられます。Pro*Cでは、EXEC SQL ALLOCATE cursor_name文を使用します。OCIでは、カーソル・データ域を使用します。

また、1つのサーバー・セッションのみで実行するアプリケーションでも、カーソル変数を使用できます。PL/SQLサブプログラムでカーソル変数を宣言してオープンし、他のPL/SQLサブプログラムのパラメータとして使用できます。

カーソル変数の例

この項では、PL/SQLでのカーソル変数の使用例を示します。


関連項目:

プログラム・インタフェースを使用するカーソル変数の例がさらに必要な場合は、次のマニュアルを参照してください。
  • 『Pro*C/C++プログラマーズ・ガイド』

  • 『Pro*COBOL Precompilerプログラマーズ・ガイド』

  • 『Oracle Call Interfaceプログラマーズ・ガイド』


例7-10では、PL/SQLカーソル変数型および2つのプロシージャを定義するパッケージを作成し、PL/SQLブロックからプロシージャを起動します。最初のプロシージャは、WHERE句にバインド変数を使用してカーソル変数をオープンします。2番目のプロシージャは、カーソル変数を使用してEMPLOYEES表の行をフェッチします。

例7-10 カーソル変数を使用するデータのフェッチ

SQL> CREATE OR REPLACE PACKAGE emp_data AS
  2
  3    TYPE emp_val_cv_type IS REF CURSOR
  4      RETURN EMPLOYEES%ROWTYPE;
  5
  6    PROCEDURE open_emp_cv (
  7      emp_cv       IN OUT emp_val_cv_type,
  8      dept_number  IN     EMPLOYEES.DEPARTMENT_ID%TYPE
  9    );
 10
 11    PROCEDURE fetch_emp_data (
 12      emp_cv   IN  emp_val_cv_type,
 13      emp_row  OUT EMPLOYEES%ROWTYPE
 14    );
 15  END emp_data;
 16  /

Package created.

SQL> CREATE OR REPLACE PACKAGE BODY emp_data AS
  2    PROCEDURE open_emp_cv (
  3      emp_cv       IN OUT emp_val_cv_type,
  4      dept_number  IN     EMPLOYEES.DEPARTMENT_ID%TYPE
  5    ) IS
  6    BEGIN
  7      OPEN emp_cv FOR
  8        SELECT * FROM EMPLOYEES
  9          WHERE DEPARTMENT_ID = dept_number;
 10    END open_emp_cv;
 11
 12    PROCEDURE fetch_emp_data (
 13      emp_cv   IN  emp_val_cv_type,
 14      emp_row  OUT EMPLOYEES%ROWTYPE
 15    ) IS
 16    BEGIN
 17      FETCH emp_cv INTO emp_row;
 18    END fetch_emp_data;
 19  END emp_data;
 20  /

Package body created.

SQL>
SQL> -- Invoke packaged procedures:
SQL>
SQL> DECLARE
  2    emp_curs     emp_data.emp_val_cv_type;
  3    dept_number  EMPLOYEES.DEPARTMENT_ID%TYPE;
  4    emp_row      EMPLOYEES%ROWTYPE;
  5
  6  BEGIN
  7    dept_number := 20;
  8
  9    -- Open cursor, using variable:
 10
 11    emp_data.open_emp_cv(emp_curs, dept_number);
 12
 13    -- Fetch and display data:
 14
 15    LOOP
 16      emp_data.fetch_emp_data(emp_curs, emp_row);
 17      EXIT WHEN emp_curs%NOTFOUND;
 18      DBMS_OUTPUT.PUT(emp_row.LAST_NAME || '  ');
 19      DBMS_OUTPUT.PUT_LINE(emp_row.SALARY);
 20    END LOOP;
 21  END;
 22  /
Hartstein  13000
Fay  6000

PL/SQL procedure successfully completed.

SQL>

例7-11では、プロシージャは、パラメータdiscrimの値に応じてEMPLOYEES表またはDEPARTMENTS表のカーソル変数をオープンします。無名ブロックは、プロシージャを起動してEMPLOYEES表のカーソル変数をオープンしますが、DEPARTMENTS表からフェッチします。このため、事前定義の例外ROWTYPE_MISMATCHが発生します。

例7-11 識別子を使用したカーソル変数

SQL> CREATE OR REPLACE PACKAGE emp_dept_data AS
  2    TYPE cv_type IS REF CURSOR;
  3
  4    PROCEDURE open_cv (
  5      cv       IN OUT cv_type,
  6      discrim  IN     POSITIVE
  7    );
  8  END emp_dept_data;
  9  /

Package created.

SQL> CREATE OR REPLACE PACKAGE BODY emp_dept_data AS
  2    PROCEDURE open_cv (
  3      cv      IN OUT cv_type,
  4      discrim IN     POSITIVE) IS
  5    BEGIN
  6      IF discrim = 1 THEN
  7        OPEN cv FOR
  8          SELECT * FROM EMPLOYEES;
  9      ELSIF discrim = 2 THEN
 10        OPEN cv FOR
 11          SELECT * FROM DEPARTMENTS;
 12      END IF;
 13    END open_cv;
 14  END emp_dept_data;
 15  /

Package body created.

SQL> DECLARE
  2    emp_rec   EMPLOYEES%ROWTYPE;
  3    dept_rec  DEPARTMENTS%ROWTYPE;
  4    cv        Emp_dept_data.CV_TYPE;
  5  BEGIN
  6    emp_dept_data.open_cv(cv, 1);  -- Open cv for EMPLOYEES fetch.
  7    FETCH cv INTO dept_rec;        -- Fetch from DEPARTMENTS.
  8    DBMS_OUTPUT.PUT(dept_rec.DEPARTMENT_ID);
  9    DBMS_OUTPUT.PUT_LINE('  ' || dept_rec.LOCATION_ID);
 10  EXCEPTION
 11    WHEN ROWTYPE_MISMATCH THEN
 12      BEGIN
 13        DBMS_OUTPUT.PUT_LINE
 14          ('Row type mismatch, fetching EMPLOYEES data ...');
 15        FETCH cv INTO emp_rec;
 16        DBMS_OUTPUT.PUT(emp_rec.DEPARTMENT_ID);
 17        DBMS_OUTPUT.PUT_LINE('  ' || emp_rec.LAST_NAME);
 18      END;
 19  END;
 20  /
Row type mismatch, fetching EMPLOYEES data ...
90  King

PL/SQL procedure successfully completed.

SQL>

PL/SQLコンパイル時のエラー処理

コンパイル時のエラーをリスト表示するには、静的データ・ディクショナリ・ビュー*_ERRORSを問い合せます。これらのビューからは、元のソース・コードを取得できます。サブプログラムのコンパイルに関するエラー・メッセージは、サブプログラムを置き換えると更新され、サブプログラムを削除すると削除されます。

SQL*Plusによって、コンパイル時のエラーに関する警告メッセージが発行されます。詳細を確認するには、SHOW ERRORSコマンドを使用する必要があります。


注意:

長い行を出力するには、SHOW ERRORS文を発行する前にSET LINESIZE文を使用してください。通常、次のように値を132に指定することをお薦めします。次に例を示します。
SET LINESIZE 132

例7-12にはコンパイル時のエラーが2つあります。WHERWHEREの誤りで、ENDの後にはセミコロンが必要です。SHOW ERRORSを使用すると、各エラーの行、列および説明が表示されます。

例7-12 コンパイル時のエラー

SQL> CREATE OR REPLACE PROCEDURE fire_emp (
  2    emp_id NUMBER
  3  ) AS
  4  BEGIN
  5    DELETE FROM EMPLOYEES
  6      WHER EMPLOYEE_ID = Emp_id;
  7  END
  8  /

Warning: Procedure created with compilation errors.

SQL> SHOW ERRORS;
Errors for PROCEDURE FIRE_EMP:

LINE/COL ERROR
-------- -----------------------------------------------------------------
5/3      PL/SQL: SQL Statement ignored
6/10     PL/SQL: ORA-00933: SQL command not properly ended
7/3      PLS-00103: Encountered the symbol "end-of-file" when expecting
         one of the following:
         ; <an identifier> <a double-quoted delimited-identifier>
         current delete exists prior <a single-quoted SQL string>
         The symbol ";" was substituted for "end-of-file" to continue.

SQL>

関連項目:

  • 静的データ・ディクショナリ・ビュー*_SOURCEの詳細は、『Oracle Databaseリファレンス』を参照してください。

  • SHOW ERRORS文の詳細は、『SQL*Plusユーザーズ・ガイドおよびリファレンス』を参照してください。


PL/SQLのランタイム・エラー処理

Oracle Databaseでは、PL/SQLコード内のユーザー定義エラーを処理できます。ユーザー定義エラーの番号およびメッセージがクライアント・アプリケーションに戻されて、そこで処理されます。

ユーザー定義エラーのメッセージは、RAISE_APPLICATION_ERRORプロシージャを使用して戻されます。次に例を示します。

RAISE_APPLICATION_ERROR(error_number, 'text', keep_error_stack)

このプロシージャはサブプログラムの実行を停止し、サブプログラムによるすべての影響をロールバックして、ユーザー定義エラー番号およびメッセージを戻します(例外ハンドラによってエラーが検出されない場合)。error_numberは、-20000〜-20999の範囲内にある必要があります。

エラー番号-20000は、ユーザーに情報を伝えることが重要で、一意のエラー番号は必要とされないメッセージの一般的な番号として使用します。テキストは、2KB以下の文字式である必要があります(それより長いメッセージは無視されます)。スタック上の既存のエラーにエラーを追加する場合はKeep_error_stackTRUEに、既存のエラーと置き換える場合はFALSEにします。デフォルトでは、このオプションはFALSEです。


注意:

DBMS_OUTPUTDBMS_DESCRIBEDBMS_ALERTなど、Oracle Databaseのパッケージの中には、-20000〜-20005の範囲のアプリケーション・エラー番号を使用するものがあります。詳細は、これらのパッケージの説明を参照してください。

RAISE_APPLICATION_ERRORプロシージャは、例外ハンドラまたは論理PL/SQLコードによく使用されます。たとえば、次の例外ハンドラは、ユーザー定義エラー・メッセージに関係する文字列を選択した後、RAISE_APPLICATION_ERRORプロシージャを起動します。

...
WHEN NO_DATA_FOUND THEN
   SELECT Error_string INTO Message
   FROM Error_table,
   V$NLS_PARAMETERS V
   WHERE Error_number = -20101 AND Lang = v.value AND
      v.parameter = "NLS_LANGUAGE";
   Raise_application_error(-20101, Message);
...

内容は次のとおりです。

例外および例外処理ルーチンの宣言

ユーザー定義例外は、そのアプリケーションに固有のエラーの処理を制御するためにPL/SQLブロック内で明示的に定義され、通知されます。例外が発生する(通知される)と、通常のPL/SQLブロックの実行は停止し、例外ハンドラと呼ばれるルーチンが起動されます。この例外ハンドラによって内部例外またはユーザー定義例外が処理されます。

アプリケーション・コードを使用すると、IF文により特に注意が必要な条件をチェックできます。エラー条件がある場合、次の2つのオプションのどちらかを選択できます。

  • 適切な例外を指示するRAISE文を入力します。RAISE文によってサブプログラムの実行は中断され、例外ハンドラがあれば制御が渡されます。

  • RAISE_APPLICATION_ERRORプロシージャを起動して、ユーザー定義エラーの番号およびメッセージを戻します。

例外ハンドラは、ユーザー定義エラー・メッセージを処理するために定義することもできます。たとえば、図7-1では、次を示しています。

  • サブプログラム内の例外およびその例外に対応する例外ハンドラ

  • エラー(預金がないのに振込みを行うなど)をチェックし、ユーザー定義エラーの番号およびメッセージをトリガーに入力する条件文

  • ユーザー定義エラー番号を起動側の環境(この場合はサブプログラム)に戻す方法、およびアプリケーションでユーザー定義エラーの番号に対応する例外を定義する方法

ユーザー定義例外は、サブプログラム本体またはパッケージ本体で宣言するか(プライベート例外)、パッケージ仕様部で宣言します(パブリック例外)。例外ハンドラは、サブプログラム本体(スタンドアロンまたはパッケージ)に定義します。

図7-1 例外およびユーザー定義エラー

例外およびユーザー定義エラー
「図7-1 例外およびユーザー定義エラー」の説明

未処理例外

データベースのPL/SQLユニットでは、適切な例外ハンドラによって検出されない未処理のユーザー・エラー条件または内部エラー条件が原因で、プログラム・ユニットの暗黙的なロールバックが発生します。プログラム・ユニットで未処理例外がある場所の前にCOMMIT文が含まれている場合、そのプログラム・ユニットの暗黙的なロールバックは直前のCOMMITまで実行されます。

さらに、データベースに格納されたPL/SQLユニットの未処理例外は、ユニットを起動するクライアント側のアプリケーションに渡されます。このアプリケーションでは、その例外はデータベースにSQL文として送られるため、アプリケーション・プログラム・ユニットの起動のみがロールバックされます(アプリケーション・プログラム・ユニット全体ではありません)。

データベースのPL/SQLユニット内の未処理例外がデータベース・アプリケーションに戻される場合は、例外を処理するためにデータベースのPL/SQLコードを変更する必要があります。アプリケーションで、データベース・プログラム・ユニットを起動したときに未処理例外を検出し、それらのエラーを処理できます。

分散問合せでのエラー処理

分散問合せは、トリガーまたはストアド・サブプログラムを使用して作成できます。この分散問合せは、ローカルのOracle Databaseインスタンスによって、対応する数のリモート問合せに分解されてリモート・ノードに送られます。リモート・ノードはその問合せを実行し、ローカル・ノードにその結果を送ります。その後、ローカル・ノードは必要な後処理を行い、ユーザーまたはアプリケーションに結果を戻します。

たとえば、制約違反のために分散問合せ文の一部でエラーが発生すると、Oracle DatabaseはORA-02055を戻します。後続する文またはサブプログラムの起動は、ロールバック、またはセーブポイントまでのロールバックが入力されるまで、ORA-02067を戻します。

分散更新の一部でエラーが発生したことを示すエラー・メッセージがチェックされるように、アプリケーションを設計してください。エラーを検出した場合、アプリケーションが処理を継続する前に、トランザクション全体をロールバック(またはセーブポイントまでロールバック)してください。

リモート・サブプログラムでのエラー処理

サブプログラムがローカルまたはリモートで実行される場合、次の例外が発生する可能性があります。

  • キーワードEXCEPTIONを使用した宣言が必要なPL/SQLのユーザー定義例外

  • NO_DATA_FOUNDなどのPL/SQL事前定義例外

  • ORA-00900などのSQLエラー

  • RAISE_APPLICATION_ERRORプロシージャを使用して生成されるアプリケーション例外

ローカル・サブプログラムを使用する場合、これらのすべてのメッセージは例外ハンドラを作成することによって検出できます。次に例外ハンドラの例を示します。

EXCEPTION
    WHEN ZERO_DIVIDE THEN
    /* Handle the exception */

なお、WHEN句の例外名は必須です。RAISE_APPLICATION_ERRORで生成される例外のように、発生した例外に名前がない場合は、プラグマPRAGMA_EXCEPTION_INITを使用して名前を割り当てることができます。次に例を示します。

DECLARE
    ...
    Null_salary EXCEPTION;
    PRAGMA EXCEPTION_INIT(Null_salary, -20101);
BEGIN
    ...
    RAISE_APPLICATION_ERROR(-20101, 'salary is missing');
    ...
EXCEPTION
    WHEN Null_salary THEN
        ...

また、リモート・サブプログラムを起動するときには、ローカル例外ハンドラを作成することによって例外を処理します。リモート・サブプログラムは、ローカルの起動側サブプログラムにエラー番号を戻す必要があります。その後、ローカル・サブプログラムは、先の例に示したように、例外を処理します。PL/SQLのユーザー定義例外は、常にローカル・サブプログラムにORA-06510を戻すため、これらの例外は処理できません。その他すべてのリモート例外は、ローカル例外と同じ方法で処理できます。

ストアド・サブプログラムのデバッグ

ストアド・サブプログラムのコンパイルにはコードの構文エラーの修正が含まれます。サブプログラムが正常に実行され、エラーが修正されていることを確認するには、追加のデバックを行う必要があります。次のようなデバッグが考えられます。

内容は次のとおりです。

PL/Scope

PL/Scopeはコンパイラ駆動方式のツールであり、PL/SQLソース・コードからユーザー定義の識別子に関するデータを収集して構成します。PL/Scopeはコンパイラ駆動方式のツールであるため、直接使用するのではなく、対話型の開発環境(SQL DeveloperやJDeveloperなど)を介して使用します。

PL/Scopeによって、強力で効率的なPL/Scopeソース・コード・ブラウザの開発が可能になります。このブラウザは、ソース・コードの参照および理解に費やされる時間を最小限にすることによって、PL/SQL開発者の生産性を向上させます。

PL/Scopeの詳細は、第8章「PL/Scopeの使用」を参照してください。

PL/SQL階層プロファイラ

PL/SQL階層プロファイラは、サブプログラム・コール別に編成されたPL/SQLプログラムの動的実行プロファイルをレポートします。SQL実行時間およびPL/SQL実行時間が個別に説明されます。動的実行プロファイルにおけるサブプログラム・レベルの各サマリーには、サブプログラムへのコール数、サブプログラム自体に要した時間、サブプログラムのサブツリー(つまり依存サブプログラム)に要した時間、詳細な親子情報などが表示されます。

生成されたHTMLレポートは任意のブラウザで参照できます。ブラウザのナビゲーション機能と厳選したリンクを組み合せた効率的な手段により、大規模なアプリケーションのパフォーマンスを分析し、アプリケーションのパフォーマンスを向上させ、開発コストを削減できます。

PL/SQL階層プロファイラの詳細は、第9章「PL/SQL階層プロファイラの使用」を参照してください。

Oracle JDeveloper

Oracle JDeveloperの最新のリリースには、PL/SQL、Javaおよびマルチ言語プログラムをデバッグする広範な機能が含まれています。各種Oracle製品の一部としてOracle JDeveloperを取得できます。通常、最新のリリースはhttp://otn.oracle.co.jp/からダウンロードできます。

DBMS_OUTPUTパッケージ

OracleパッケージDBMS_OUTPUTを使用すると、ストアド・サブプログラムおよびトリガーもデバッグできます。コードにはPUT文およびPUT_LINE文を入れて、変数および式の値を端末に出力します。

PL/SQLストアド・サブプログラムおよびJavaストアド・サブプログラムのデバッグ権限

Oracle Database 10gから、データベース内で実行されるPL/SQLおよびJavaコードのデバッグに新しい権限モデルが適用されるようになりました。このモデルは、Oracle JDeveloper、Oracle Developer、または各種サード・パーティのPL/SQLやJavaの開発環境のいずれを使用していても適用され、DBMS_DEBUG APIおよびDBMS_DEBUG_JDWP APIの両方に影響します。

デバッガに接続するセッションでは、DEBUG CONNECT SESSIONシステム権限を所有するユーザーが接続操作を実行する必要があります。接続コールに関係するDRルーチンの所有者であるユーザーも、この操作を実行できます。

デバッガがセッションに接続されると、セッション・ログイン・ユーザーおよび現在有効なセッションレベルのロールが、このデバッグ用の接続の権限環境として決定されます。デバッグに必要なすべてのDEBUG権限またはEXECUTE権限を、このユーザーとロールの組合せに付与する必要があります。

  • Javaのパブリック変数またはPL/SQLのパッケージ仕様部内に宣言された変数を表示および変更できるようにするために、関連するコードのEXECUTE権限またはDEBUG権限をデバッグ用の接続に付与する必要があります。

  • プライベート変数またはブレークポイントを表示および変更し、コードを1行ずつ実行できるようにするために、関連するコードのDEBUG権限を付与する必要があります。


    注意:

    DEBUG権限を付与することにより、デバッグ対象のサブプログラムが実行するようにコーディングされている処理をデバッグ・セッションで実行することが可能になります。

このような権限要件に加えて、コードの各行で停止する機能とデバッガからの変数へのアクセスを実行できるのは、生成されたデバッグ情報とともにコンパイルされているコード内のみという制限があります。ALTER PACKAGEなどの文でPL/SQLコンパイル・パラメータPLSQL_DEBUGおよびDEBUGキーワードを使用し、PL/SQLコンパイラで結果にデバッグ情報を含めるかどうかを制御できます。デバッグ情報を含めない場合、変数にはアクセスできず、コードの各行でステップ実行やブレークポイントを指定することもできません。PL/SQLコンパイラでは、PL/SQLのwrapユーティリティにより非表示になっているコードに対しては、デバッグ情報は生成されません。


関連項目:

wrapユーティリティの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

DEBUG ANY PROCEDUREシステム権限の効力は、データベース内のすべてのオブジェクトにDEBUG権限を付与したときの効力と同じです。O7_DICTIONARY_ACCESSIBILITYパラメータの値がTRUEである場合は、SYSが所有しているオブジェクトにも付与します。

デバッグ・ロール・メカニズムを使用すると、セッションでは通常使用できない、デバッグ用の権限を保持できます。デバッグ・ロールおよび必要な関連パスワードの指定方法の詳細は、マニュアルでDBMS_DEBUGパッケージおよびDBMS_DEBUG_JDWPパッケージについての説明を参照してください。

JAVADEBUGPRIVロールは、DEBUG CONNECT SESSION権限およびDEBUG ANY PROCEDURE権限を保持します。必要な場合にのみ、これらの権限をこのロールに付与してください。


注意:

SYSが所有するオブジェクトにDEBUG ANY PROCEDURE権限またはDEBUG権限を付与することは、すべての権限をデータベースに付与することを意味します。

低レベルのデバッグ・コードの書込み

デバッガの一部のコードを書き込む場合は、DBMS_DEBUG_JDWPDBMS_DEBUGなどのパッケージを使用する必要があります。

DBMS_DEBUG_JDWPパッケージ

Oracle9iリリース2から提供されているDBMS_DEBUG_JDWPパッケージは、将来DBMS_DEBUGパッケージと置き換わるマルチ言語デバッグ用のフレームワークを提供します。PL/SQLとJavaの組合せのプログラムには特に有効です。

DBMS_DEBUGパッケージ

Oracle8iから提供されているDBMS_DEBUGパッケージでは、サーバー側のデバッガが実装されており、サーバー側のPL/SQLユニットをデバッグする方法を提供します。Oracle Procedure Builderやその他の様々なサード・パーティ・ベンダーが提供するソリューションなどデバッガのいくつかでは、このAPIが使用されています。


関連項目:

  • 『Oracle Procedure Builder Developer's Guide』

  • DBMS_DEBUGパッケージおよび関連する権限の詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

  • DBMS_OUTPUTパッケージおよび関連する権限の詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

  • DBMS_DEBUG_JDWPパッケージの使用方法の詳細は、Oracle JDeveloperのドキュメントを参照してください。

  • 権限の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

  • 低レベルのデバッグ・コードの書込みの詳細は、http://otn.oracle.co.jp/のPL/SQLページを参照してください。


ストアド・サブプログラムの起動

ストアドPL/SQLサブプログラムは、次のように様々な環境から起動できます。次に例を示します。

ストアドPL/SQLファンクション(プロシージャではない)は、SQL文から起動することもできます。詳細は、「SQL文からのストアドPL/SQLファンクションの起動」を参照してください。

内容は次のとおりです。


関連項目:

  • パラメータの受渡しも含めたPL/SQLサブプログラムの起動の詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。
  • トリガー本体のコーディングの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。


サブプログラムの起動に必要な権限

次のようなサブプログラムを起動する場合、権限は必要ありません。

  • 所有しているスタンドアロン・サブプログラム

  • 所有しているパッケージ内のサブプログラム

  • パブリック・スタンドアロン・サブプログラム

  • パブリック・パッケージ内のサブプログラム

別のユーザーが所有するスタンドアロン・サブプログラムまたはパッケージ・サブプログラムを起動する場合は、次のようになります。

  • スタンドアロン・サブプログラムまたはサブプログラムを含むパッケージのEXECUTE権限、またはEXECUTE ANY PROCEDUREシステム権限が必要です。

  • リモート・サブプログラムを実行する場合は、EXECUTE権限またはEXECUTE ANY PROCEDUREシステム権限が、ロールを介してではなく直接付与されている必要があります。

  • 起動の際に所有者の名前を指定する必要があります。次に例を示します。

    EXECUTE jdoe.Fire_emp (1043);
    EXECUTE jdoe.Hire_fire.Fire_emp (1043);
    
  • サブプログラムが定義者権限(DR)サブプログラムである場合、このサブプログラムはその所有者の権限を使用して実行されます。所有者は、参照オブジェクトに必要なオブジェクト権限をすべて所有している必要があります。

  • サブプログラムが実行者権限(IR)サブプログラムである場合、このサブプログラムはユーザーの権限を使用して実行されます。すべての参照オブジェクト、つまり自スキーマ内で変換される外部参照を介してサブプログラムがアクセスするオブジェクトに対して、必要なオブジェクト権限をすべて所有している必要があります。これらの権限は直接、またはロールを介して保持できます。IRサブプログラムが直接、または間接的にDRサブプログラムから起動されないかぎり、ロールは有効です。

Oracleツールを使用した対話形式によるサブプログラムの起動

サブプログラムは、SQL*Plusなど、Oracle Databaseのツール製品から対話形式で起動できます。例7-13では、SQL*Plusを使用してプロシージャを作成し、2つの方法で起動します。

例7-13 SQL*Plusを使用した対話形式によるサブプログラムの起動

SQL> CREATE OR REPLACE PROCEDURE salary_raise (
  2    employee  EMPLOYEES.EMPLOYEE_ID%TYPE,
  3    raise     EMPLOYEES.SALARY%TYPE
  4  ) IS
  5  BEGIN
  6    UPDATE EMPLOYEES
  7      SET SALARY = SALARY + raise
  8        WHERE EMPLOYEE_ID = employee;
  9  END;
 10  /

Procedure created.

SQL>
SQL> -- Invoke procedure from within PL/SQL block:
SQL>
SQL> BEGIN
  2    salary_raise(205, 200);
  3  END;
  4  /

PL/SQL procedure successfully completed.

SQL> -- Invoke procedure with EXECUTE statement:
SQL>
SQL> EXECUTE salary_raise(205, 200);

PL/SQL procedure successfully completed.

SQL>

一部の対話形式ツールでは、セッション中に使用できるセッション変数を作成することができます。例7-14では、SQL*Plusを使用してセッション変数の作成、使用および出力を行います。

例7-14 SQL*Plusを使用したセッション変数の作成と使用

SQL> -- Create function for later use:
SQL>
SQL> CREATE OR REPLACE FUNCTION get_job_id (
  2    emp_id  EMPLOYEES.EMPLOYEE_ID%TYPE
  3  ) RETURN EMPLOYEES.JOB_ID%TYPE
  4  IS
  5    job_id  EMPLOYEES.JOB_ID%TYPE;
  6  BEGIN
  7    SELECT JOB_ID INTO job_id
  8      FROM EMPLOYEES
  9        WHERE EMPLOYEE_ID = emp_id;
 10    RETURN job_id;
 11  END;
 12  /

Function created.

SQL>
SQL> -- Create session variable:
SQL>
SQL> VARIABLE job VARCHAR2(10);
SQL>
SQL> -- Execute function and store returned value in session variable:
SQL>
SQL> EXECUTE :job := get_job_id(204);

PL/SQL procedure successfully completed.

SQL>
SQL> PRINT job;

JOB
--------------------------------
PR_REP

SQL>

関連項目:

  • EXECUTEコマンドの詳細は、『SQL*Plusユーザーズ・ガイドおよびリファレンス』を参照してください。

  • 開発ツールを使用して同様の操作を実行する詳細は、ご使用のツール製品のドキュメントを参照してください。


別のサブプログラムからのサブプログラムの起動

ストアド・サブプログラムは、別のサブプログラムやトリガーから起動できます。例7-15では、プロシージャprint_mgr_nameがプロシージャprint_emp_nameを起動します。

サブプログラムの再帰的な起動が可能です(つまり、サブプログラムがサブプログラム自体を起動できます)。

例7-15 別のサブプログラムからのサブプログラムの起動

SQL> -- Create procedure that takes employee's ID and prints employee's name:
SQL>
SQL> CREATE OR REPLACE PROCEDURE print_emp_name (
  2    emp_id  EMPLOYEES.EMPLOYEE_ID%TYPE
  3  )
  4  IS
  5    fname  EMPLOYEES.FIRST_NAME%TYPE;
  6    lname  EMPLOYEES.LAST_NAME%TYPE;
  7  BEGIN
  8    SELECT FIRST_NAME, LAST_NAME
  9      INTO fname, lname
 10        FROM EMPLOYEES
 11          WHERE EMPLOYEE_ID = emp_id;
 12
 13    DBMS_OUTPUT.PUT_LINE (
 14      'Employee #' || emp_id || ':  ' || fname || ' ' || lname
 15    );
 16  END;
 17  /

Procedure created.

SQL> -- Create procedure that takes employee's ID and prints manager's name:
SQL>
SQL> CREATE OR REPLACE PROCEDURE print_mgr_name (
  2    emp_id  EMPLOYEES.EMPLOYEE_ID%TYPE
  3  )
  4  IS
  5    mgr_id  EMPLOYEES.MANAGER_ID%TYPE;
  6  BEGIN
  7    SELECT MANAGER_ID
  8      INTO mgr_id
  9        FROM EMPLOYEES
 10          WHERE EMPLOYEE_ID = emp_id;
 11
 12    DBMS_OUTPUT.PUT_LINE (
 13      'Manager of employee #' || emp_id || ' is:  '
 14    );
 15
 16    print_emp_name(mgr_id);
 17  END;
 18  /

Procedure created.

SQL> BEGIN
  2    print_emp_name(200);
  3    print_mgr_name(200);
  4  END;
  5  /
Employee #200:  Jennifer Whalen
Manager of employee #200 is:
Employee #101:  Neena Kochhar

PL/SQL procedure successfully completed.

SQL>

3GLアプリケーションからのサブプログラムの起動

3GLデータベース・アプリケーション(プリコンパイラまたはOCIアプリケーションなど)では、コード内からサブプログラムを起動することができます。

プロシージャFire_emp1が次のように作成されているとします。

CREATE OR REPLACE PROCEDURE fire_emp1 (Emp_id NUMBER) AS
  BEGIN
    DELETE FROM Emp_tab WHERE Empno = Emp_id;
  END;

プリコンパイラ・アプリケーションからサブプログラムを実行するには、EXECコール・インタフェースを使用する必要があります。たとえば、次の文は、プリコンパイラ・アプリケーションのコード内でFire_empプロシージャを起動します。

EXEC SQL EXECUTE
  BEGIN
    Fire_emp1(:Empnum);
  END;
END-EXEC;

関連項目:

3GLアプリケーション内部からのPL/SQLサブプログラムの起動方法の詳細は、次のマニュアルを参照してください。
  • 『Oracle Call Interfaceプログラマーズ・ガイド』

  • 『Pro*C/C++プログラマーズ・ガイド』


リモート・サブプログラムの起動

リモート・サブプログラム(スタンドアロン・サブプログラムおよびパッケージ・サブプログラム)は、そのリモート・サブプログラム名、データベース・リンクおよびリモート・サブプログラムのパラメータを指定することによって、サブプログラム、OCIアプリケーションまたはプリコンパイラの中から起動できます。

たとえば、次のSQL*Plus文はプロシージャfire_emp1を起動します。このプロシージャはデータベース内にあり、boston_serverという名前のローカル・データベース・リンクによって参照されています。

EXECUTE fire_emp1@boston_server(1043);

デフォルト値がある場合でも、すべてのリモート・サブプログラムのパラメータに対して値を指定する必要があります。リモート・パッケージ変数および定数にはアクセスできません。


注意:

  • リモート・サブプログラムの起動では、実行時バインディングが使用されます。接続するユーザー・アカウントは、データベース・リンクに依存します。(ストアド・サブプログラムではコンパイル時バインディングが使用されます。)

  • ローカル・サブプログラムがリモート・サブプログラムを起動する場合、ローカル・サブプログラムの実行中にタイムスタンプの不一致が検出されると、リモート・サブプログラムは実行されず、ローカル・サブプログラムが無効になります。


内容は次のとおりです。


関連項目:

リモート・サブプログラム起動時の例外処理の詳細は、「リモート・サブプログラムでのエラー処理」を参照してください。

リモート・サブプログラムのシノニム

リモート・サブプログラム名とデータベース・リンクのシノニムを作成し、シノニムを使用してサブプログラムを起動することができます。次に例を示します。

CREATE SYNONYM synonym1 for fire_emp1@boston_server;

EXECUTE synonym1(1043);
/

シノニムによって、サブプログラム、OCIアプリケーションまたはプリコンパイラのみでなく、SQL*Formsアプリケーションなど、Oracle Databaseのツール製品のアプリケーションからリモート・サブプログラムを起動できるようになります。

シノニムによってデータの独立性と場所の透過性の両方が実現されます。シノニムを使用すると、オブジェクトを所有するユーザーやオブジェクトを格納するデータベースに関係なく、アプリケーションを変更せずに使用できます。ただし、シノニムはデータベース・オブジェクトに対する権限の代用とはなりません。ユーザーがシノニムを使用するためには、そのユーザーが適切な権限を付与されている必要があります。

パッケージ内に定義されているサブプログラムは個別のオブジェクトではないため(パッケージはオブジェクトです)、パッケージ内の個々のサブプログラムのシノニムは作成できません。

シノニムを使用しない場合は、リモート・サブプログラムを起動するローカル・サブプログラムを作成することもできます。次に例を示します。

CREATE OR REPLACE PROCEDURE local_procedure
  (arg IN NUMBER)
AS
BEGIN
  fire_emp1@boston_server(arg);
END;
/
DECLARE
  arg NUMBER;
BEGIN
  local_procedure(arg);
END;
/

関連項目:

  • シノニムの詳細は、『Oracle Database概要』を参照してください。

  • CREATE SYNONYM文の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。


トランザクションのコミット

リモートのストアド・サブプログラムの起動は、更新処理を前提とします。このためこのような参照でも、常に2フェーズ・コミットのトランザクションが必要です(リモート・サブプログラムが読取り専用の場合を含む)。また、リモート・サブプログラムの起動を含むトランザクションをロールバックする場合、そのリモート・サブプログラムによって実行された処理も同時にロールバックされます。

リモートで起動されたサブプログラムでは通常、ローカル・サブプログラムと同じように、COMMITROLLBACKSAVEPOINTの各文を実行できます。ただし、次のように、アクションに少し違いがあります。

  • トランザクションがOracle Database以外のデータベースによって開始された場合(XAアプリケーションなどの場合)、リモート・サブプログラムでこれらの操作はできません。

  • これらの操作の1つを実行すると、リモート・サブプログラムは独自の分散トランザクションを開始できません。

  • リモート・サブプログラムがその作業をコミットまたはロールバックしない場合、データベース・リンクがクローズした時点で暗黙的にコミットが実行されます。このとき、トランザクションは実行中とみなされるため、リモート・サブプログラムを起動できません。

分散更新ではデータは複数のデータベースで更新されます。異なるデータベースのデータにアクセスする複数のリモート更新を含むサブプログラムを使用できます。構文内の文はリモート・データベースに送信され、構文の実行はユニット単位で正常終了または異常終了します。分散更新の一部がエラーとなり、一部が正常終了した場合、処理を続けるには(トランザクション全体またはセーブポイントまでの)ロールバックが必要です。分散更新を実行するサブプログラムを作成する場合は、この点を考慮する必要があります。

SQL文からのストアドPL/SQLファンクションの起動


注意:

SQLは命令型(または手続き型)言語ではなく宣言型言語であるため、ファンクションが命令型言語のPL/SQLで記述されていても、SQL文から起動されるファンクションが何回実行されるかはわかりません。

アプリケーションでファンクションが特定の回数実行されることが必要な場合には、SQL文からファンクションを起動しないでください。かわりにカーソルを使用します。

たとえば、選択した行ごとに1回ずつファンクションをコールすることがアプリケーションで必要な場合は、カーソルをオープンして、カーソルから行を選択し、各行に対してファンクションをコールします。このようにすると、ファンクションをコールする回数が、カーソルからフェッチされる行数と同じになります。


ストアドPL/SQLファンクションをSQL文から起動するには、そのファンクションをスキーマ・レベルまたはパッケージ仕様部で宣言する必要があります。

ストアドPL/SQLファンクションを起動できるSQL文は次のとおりです。

SQLからPL/SQLサブプログラムを起動するには、その所有者であるか、またはそのサブプログラムに対するEXECUTE権限を保持している必要があります。PL/SQLファンクションを使用して定義されているビューから選択するには、そのビューに対するSELECT権限が必要です。そのビューからの選択には、別のEXECUTE権限は必要ありません。

パラメータを渡す方法など、PL/SQLサブプログラムを起動する方法の概要は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

内容は次のとおりです。

SQL文からストアドPL/SQLサブプログラムを起動する理由

SQL文からPL/SQLサブプログラムを起動すると、次のような利点があります。

  • SQLの拡張によって、ユーザーの生産性が向上します。

    実行する内容がSQL文のみで表現するには複雑すぎたり、非常に扱いにくかったり、不可能な場合に、SQL文の表現機能が強化されます。

  • 問合せの効率を向上します。

    問合せのWHERE句にファンクションを指定すると、条件を使用してデータをフィルタできます。ファンクションを使用できない場合は、アプリケーションで評価する必要があります。

  • 特殊なデータ型(緯度、経度、温度など)を表すための文字列を操作できます。

  • パラレル問合せを実行できます。

    問合せがパラレル化されると、PL/SQLサブプログラム内のSQL文も(パラレル問合せオプションを使用して)パラレルに実行できます。

SQL文の中でPL/SQLファンクションを使用できる箇所

SQL文では、組込みSQLファンクションや式を使用できる任意の箇所でPL/SQLファンクションを使用できます。たとえば、次のような箇所にPL/SQLファンクションを使用できます。

  • SELECT文の選択リスト

  • WHERE句またはHAVING句の条件

  • CONNECT BY句、START WITH句、ORDER BY句またはGROUP BY

  • INSERT文のVALUES

  • UPDATE文のSET

SELECT文では、次のかわりにPL/SQL表ファンクション(複数の行をまとめて戻す)を使用できます。

  • SELECTリストの列名

  • FROM句の表名

不変の定義が必要な次のようなコンテキストには、PL/SQLファンクションは使用できません。

  • CREATE文またはALTER TABLE文のCHECK制約句

  • 列に対するデフォルト値の指定

SQL式の中でPL/SQLファンクションを使用できる条件

PL/SQLファンクションをSQL式から起動するには、そのファンクションが次の要件を満たしている必要があります。

  • 列(グループ)ファンクションではなく、行ファンクションであること。つまり、列全体をその引数としてとることはできません。

  • 仮パラメータが、OUTパラメータやIN OUTパラメータではなく、INパラメータであること。

  • 仮パラメータおよび戻り値(存在する場合)のデータ型が、PL/SQLデータ型(BOOLEANRECORDTABLEなど)ではなく、Oracleの組込みデータ型(CHARDATENUMBERなど)であること。

    ただし、このルールの例外として、対応する実パラメータが暗黙的に仮パラメータのデータ型に変換される場合には、その仮パラメータはPL/SQLデータ型を持つことができます(例7-17を参照してください)。

例7-16のファンクションは、これらの要件を満たしています。

例7-16 SQL式の中で使用できるPL/SQLファンクション

SQL> DROP TABLE payroll;
DROP TABLE payroll
           *
ERROR at line 1:
ORA-00942: table or view does not exist


SQL> CREATE TABLE payroll (
  2    srate  NUMBER,
  3    orate  NUMBER,
  4    acctno NUMBER
  5  );

Table created.

SQL> CREATE OR REPLACE FUNCTION gross_pay (
  2    emp_id  IN NUMBER,
  3    st_hrs  IN NUMBER DEFAULT 40,
  4    ot_hrs  IN NUMBER DEFAULT 0
  5  ) RETURN NUMBER
  6  IS
  7    st_rate  NUMBER;
  8    ot_rate  NUMBER;
  9  BEGIN
 10    SELECT srate, orate
 11      INTO st_rate, ot_rate
 12        FROM payroll
 13          WHERE acctno = emp_id;
 14
 15     RETURN st_hrs * st_rate + ot_hrs * ot_rate;
 16  END gross_pay;
 17  /

Function created.

SQL>

例7-17のSQL*Plusスクリプトでは、SQL文CALLによりPL/SQLファンクションf1が起動されます。このファンクションの仮パラメータと戻り値のデータ型はPL/SQLデータ型PLS_INTEGERです。実パラメータ2は暗黙的にデータ型PLS_INTEGERへ変換されるため、このCALL文は正常に実行されます。実パラメータの値がPLS_INTEGERの範囲外であれば、このCALL文は正常には実行されません。

例7-17 SQL式から起動された、PL/SQLデータ型の仮パラメータを持つPL/SQLファンクション

SQL> CREATE OR REPLACE FUNCTION f1 (
  2    b IN PLS_INTEGER
  3  ) RETURN PLS_INTEGER
  4  IS
  5  BEGIN
  6    RETURN
  7      CASE
  8        WHEN b > 0  THEN  1
  9        WHEN b <= 0 THEN -1
 10        ELSE NULL
 11      END;
 12  END f1;
 13  /

Function created.

SQL> VARIABLE x NUMBER;
SQL> CALL f1(b=>2) INTO :x;

Call completed.

SQL> /

Call completed.

SQL> PRINT x;

         X
----------
         1

SQL>

副作用の制御

ストアド・サブプログラムの純粋度とは、データベース表またはパッケージ変数に対してそのサブプログラムが及ぼす副作用のことをいいます。副作用によって、問合せのパラレル処理が妨害されたり、処理順序に依存する(したがって、不確定な)結果が発生したり、ユーザー・セッションにまたがったパッケージ状態のメンテナンスが必要になります。ファンクションがSQL問合せまたはDML文から起動される場合は、様々な副作用は受け入れられません。

Oracle8iより前のリリースでは、Oracle DatabaseはPL/SQLコンパイラを使用して、ストアド・サブプログラムまたはSQL文のコンパイル中に制限を施行していました。Oracle8iからは、コンパイル時の制限は緩和され、実行中の制限も少なくなっています。

この変更によって、PL/SQL、JavaおよびCで作成されたストアド・サブプログラムが統一してサポートされ、プログラマには最大限の柔軟性が提供されています。

内容は次のとおりです。

制限事項

SQL文が実行されるとき、すでに実行中のSQL文の中にこのSQL文が論理的に埋め込まれているかどうかが確認されます。文がトリガーから、またはすでに実行中のSQL文から起動されたサブプログラムから実行されると、この確認が行われます。このような場合は、新しいSQL文が特定のコンテキスト内で安全かどうかを判断するために、さらに確認が行われます。

サブプログラムには次の制限が適用されます。

  • 問合せまたはDML文から起動されるサブプログラムは、現行のトランザクションの終了、セーブポイントの作成またはセーブポイントまでのロールバック、あるいはシステムまたはセッションの変更(ALTER)を実行できません。

  • 問合せ(SELECT)文またはパラレル化されたDML文から起動されるサブプログラムは、DML文を実行できません。またはデータベースを変更できません。

  • DML文から起動されるサブプログラムでは、そのDML文が変更中の表の読取りおよび変更はできません。

前述のすべての制限は、サブプログラムまたはトリガー内でSQL文が実行される方法にかかわらず適用されます。次に例を示します。

  • 前述の制限は、PL/SQLから起動されるSQL文(サブプログラムまたはトリガーの本体に直接埋め込まれている文かどうかにかかわらず)が、ネイティブ動的メカニズム(EXECUTE IMMEDIATE)を使用して実行されるか、またはDBMS_SQLパッケージを使用して実行される場合、そのSQL文に適用されます。

  • SQLJ構文を使用してJavaに埋め込まれている文、またはJDBCを使用して実行される文に適用されます。

  • 外部C関数内からコールバック・コンテキストを使用してOCIで実行される文に適用されます。

新しいSQL文の実行が、すでに実行中の文のコンテキストに論理的に埋め込まれていない場合は、前述の制約を回避できます。そのような回避方法の1つとして、PL/SQLの自律型トランザクションの使用があります。(「自律型トランザクション」を参照)。外部C関数からOracle Call Interface(OCI)を使用するという別の回避方法もあります。この場合は、OCIExtProcContext引数から使用できるハンドルを使用せず、新しい接続を作成します。

ファンクションの宣言

キーワードDETERMINISTICおよびPARALLEL_ENABLEを、ファンクションを宣言する構文内で使用できます。この2つのキーワードは最適化ヒントで、問合せオプティマイザおよび他のソフトウェア・コンポーネントに対して、次のファンクションについての情報を提供します。

  • 重複して起動する必要のないファンクション

  • パラレル問合せまたはDML文の中で許可されているファンクション

ファンクション索引および特定のスナップショットやマテリアライズド・ビューで使用できるのは、DETERMINISTICを指定したファンクションのみです。

確定的な(DETERMINISTIC)ファンクションは、引数として渡される値のみに依存し、パッケージ変数またはデータベースの内容を参照したり変更することがなく、また、他の副作用を持ちません。このようなファンクションは、渡される引数値の組合せが同じであるかぎり、同じ戻り値を生成します。

the DETERMINISTICキーワードは、ファンクションの宣言の中で戻り値の型の後に入れます。次に例を示します。

SQL> CREATE OR REPLACE FUNCTION f1 (
  2    p1 NUMBER
  3  ) RETURN NUMBER DETERMINISTIC
  4  IS
  5  BEGIN
  6    RETURN p1 * 2;
  7  END;
  8  /

Function created.

SQL>

このキーワードは、次の場所に挿入できます。

  • CREATE FUNCTION文で定義されたファンクション

  • CREATE PACKAGE文で宣言されたファンクション

  • CREATE TYPE文のメソッド宣言

CREATE PACKAGE BODY文またはCREATE TYPE BODY文のファンクション本体またはメソッド本体には、キーワードは配置できません。

DETERMINISTICとマークされているファンクションの起動では、他にアクションを実行しなくても、パフォーマンスがある程度最適化されます。次の機能では、その機能で使用されるすべてのファンクションをDETERMINISTICとして宣言する必要があります。

  • ファンクション索引で使用されるすべてのユーザー定義ファンクション

  • 高速リフレッシュ対象、またはENABLE QUERY REWRITEとしてマークされるマテリアライズド・ビューで使用されるファンクション

これらの機能では、ファンクションを起動するのではなく、事前に計算されている結果をできるかぎり使用しようとします。

プログラミングにおいては、次のDETERMINISTICカテゴリに属するファンクションを作成するのが適切です。

  • WHERE句、ORDER BY句またはGROUP BY句の中で使用されるファンクション

  • SQL型のMAPメソッドまたはORDERメソッドであるファンクション

  • 結果セットに行を入れるかどうか、またはどこに入れるかを決定するファンクション

DETERMINISTICを作成する場合は、次の点に注意してください。

  • ファンクションのアクションが本当に確定的であるかどうかをデータベースで認識することはできません。そのアクションが本当の意味で確定的ではないファンクションに対してDETERMINISTICキーワードが適用されると、そのファンクションが関係する問合せの結果は予測できません。

  • DETERMINISTICファンクションのセマンティックを変更し、再コンパイルすると、既存のファンクション索引およびマテリアライズド・ビューにより、旧バージョンのファンクションの結果がレポートされます。このように、ファンクションのセマンティックを変更する場合、依存するファンクション索引およびマテリアライズド・ビューを手動で作成する必要があります。


関連項目:

CREATE FUNCTIONの制約は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

パラレル問合せおよびパラレルDML

Oracle Databaseのパラレル実行機能により、SQL文の実行作業が複数のプロセスにわたって分割されます。パラレルで実行されるSQL文から起動されるファンクションは、各プロセス内で実行される個別のコピーを持つことができ、それぞれのコピーは、そのプロセスによって処理される行のサブセットのためにのみ起動されます。

各プロセスには、そのプロセス専用のパッケージ変数のコピーがあります。パラレル実行が開始されると、コピーされたパッケージ変数は、新しいユーザーがシステムにログインするときのように、パッケージ仕様部および本体の情報に基づいて初期化されます。パッケージ変数内の値は、元のログイン・セッションからはコピーされません。パッケージ変数に対する変更は、多数のセッション間で伝播したり、元のセッションに伝播することはありません。JavaのSTATICクラス属性は、同様に、各プロセス内で独立して初期化され変更されます。ファンクションでは、検出される様々な行の値をパッケージ(またはJavaのSTATIC)変数を使用して蓄積できるため、Oracle Databaseでは、すべてのユーザー定義ファンクションの実行をパラレル化しても安全であるとはいえません。

リリース8.1.5より前のOracle DatabaseのSELECT 文では、パラレル問合せ最適化機能により、PRAGMA RESTRICT_REFERENCES宣言内にRNPSおよびWNPSとして記述されたファンクションは、パラレルで実行できました。CREATE FUNCTION文を使用して定義されているファンクションでは、ファンクションが十分に純粋であるかどうかを判断するために、暗黙的にコードが調べられていました。パラレル実行は、これらのファンクションに対してプラグマを指定できない場合でも、発生する可能性があります。

DML文の場合、8.1.5より前のリリースのOracle Databaseでは、ファンクションがPRAGMA RESTRICT_REFERENCES宣言内でRNDSWNDSRNPSおよびWNPSの4つすべてが記されているかどうかは、パラレル化最適化機能により調べられていました。データベースまたはパッケージ変数のいずれかに対して読取りでもなく書込みでもないとマークされたファンクションは、パラレルで実行できました。ここでも、CREATE FUNCTION文を使用して定義されているファンクションでは、ファンクションが実際に十分に純粋であるかどうかを判断するために、明示的にコードが調べられていました。パラレル実行は、これらのファンクションに対してプラグマを指定できない場合でも発生する可能性があります。

Oracle Databaseリリース8.1.5以降でも、以前のバージョンでパラレル化可能として認識されていたファンクションは、引き続きパラレル化されます。コードをパラレル実行用として安全であるとマークする方法として、PARALLEL_ENABLEキーワードをお薦めします。このキーワードは、「ファンクションの宣言」で説明しているDETERMINISTICと構文的に類似しています。このキーワードは、次に示すように、ファンクション宣言の戻り値型の後に指定します。

SQL> CREATE OR REPLACE FUNCTION f1 (
  2    p1 NUMBER
  3  ) RETURN NUMBER PARALLEL_ENABLE
  4  IS
  5  BEGIN
  6    RETURN p1 * 2;
  7  END;
  8  /

Function created.

SQL>

CREATE FUNCTIONを使用して定義されるPL/SQLファンクションは、そのファンクションがパッケージ変数の読取りも書込みを行わず、パッケージ変数の読取りまたは書込みを行う可能性のあるファンクションも起動しないことをシステムで判断できる場合は、パラレルで実行しても安全であると明示的に宣言しなくても、パラレルで実行できます。JavaメソッドまたはC関数は、プログラマがコール仕様にPARALLEL_ENABLEと明示的に指定するか、またはPRAGMA RESTRICT_REFERENCESを指定してファンクションが十分に純粋であることを示さないかぎり、システムはパラレルでの実行が安全であるとはみなしません。

パラレルDML文の一部としてパラレル実行されるファンクションに対しては、追加のランタイム制約が設けられています。このようなファンクションは、DML文の実行を許可されません。検索(SELECT)文の中で実行されるファンクションに対して適用される制約と同じ制約を受けます。


関連項目:

制限事項

下位互換性のためのPRAGMA RESTRICT_REFERENCES

8.1.5(Oracle8i)より前のOracle Databaseバージョンでは、プログラマはサブプログラムの純粋度レベルの宣言にPRAGMA RESTRICT_REFERENCESを使用していました。それ以降のバージョンでは、かわりにヒントPARALLEL_ENABLEおよびDETERMINISTICを使用して、サブプログラムの純粋度についてOracle Databaseと通信します。

コードからPRAGMA RESTRICT_REFERENCESを削除できます。ただし、このプラグマは次のような下位互換性が必要となる場合に備えて残されています。

  • 既存コードを編集してPRAGMA RESTRICT_REFERENCESをすべて削除することは不可能かつ非現実的です。別のサブプログラムS2に依存するサブプログラムS1からPRAGMA RESTRICT_REFERENCESを削除しない場合、PRAGMA RESTRICT_REFERENCESはS2でも必要になる場合があるため、S1はコンパイルします。

  • 既存コード中のPRAGMA RESTRICT_REFERENCESをヒントPARALLEL_ENABLEおよびDETERMINISTICに置き換えると、新しい依存コードのアクションに悪影響を与える可能性があります。既存コードのアクションを保持するために、PRAGMA RESTRICT_REFERENCESを使用してください。

既存のPL/SQLアプリケーションでは、既存コードとの統合を容易にするために、新しいファンクションに対してもプラグマを引き続き使用することができます。新しいアプリケーションではプラグマは使用しないでください。

PRAGMA RESTRICT_REFERENCESを使用する場合は、パッケージ本体ではなく、パッケージ仕様部に含めます。このプラグマは、サブプログラムの宣言の後に置く必要がありますが、直後に置く必要はありません。所定のサブプログラム宣言を参照できるプラグマは、1つのみです。

PRAGMA RESTRICT_REFERENCESをコーディングするには、次の構文を使用します。

PRAGMA RESTRICT_REFERENCES (
    Function_name, WNDS [, WNPS] [, RNDS] [, RNPS] [, TRUST] );

パラメータは次のとおりです。

オプション 説明
WNDS サブプログラムはデータベース状態を書き込みません(データベース表を変更しないということです)
RNDS サブプログラムはデータベース状態を読み込みません(データベース表を問い合せないということです)
WNPS サブプログラムはパッケージ状態を書き込みません(パッケージ変数の値を変更しないということです)
RNPS サブプログラムはパッケージ状態を読み込みません(パッケージ変数の値を参照しないということです)
TRUST プラグマにリストされている制限は適用されません。真であると判断できます。これにより、RESTRICT_REFERENCES宣言を持つファンクションから、この宣言を持たないファンクションを起動することが容易になります。

引数はどんな順序でも渡せます。サブプログラム本体にあるSQL文のいずれかが規則に違反する場合は、その文が解析されるときにエラーが発生します。

例7-18では、ファンクションcompoundにより、データベースまたはパッケージ状態の読取りおよび書込みは行われないため、最大の純粋度レベルを宣言できます。PL/SQLコンパイラがサブプログラムを必要以上に拒否することがないように、サブプログラムで可能な最大の純粋度レベルを常に宣言してください。

例7-18 PRAGMA RESTRICT_REFERENCES

SQL> DROP TABLE accounts;
DROP TABLE accounts
           *
ERROR at line 1:
ORA-00942: table or view does not exist

SQL> CREATE TABLE accounts (
  2    acctno   INTEGER,
  3    balance  NUMBER
  4  );

Table created.

SQL>
SQL> INSERT INTO accounts
  2    VALUES (12345, 1000.00);

1 row created.

SQL>
SQL> CREATE OR REPLACE PACKAGE finance AS
  2    FUNCTION compound (
  3      years  IN NUMBER,
  4      amount IN NUMBER,
  5      rate   IN NUMBER
  6    ) RETURN NUMBER;
  7    PRAGMA RESTRICT_REFERENCES (compound, WNDS, WNPS, RNDS, RNPS);
  8  END finance;
  9  /

Package created.

SQL> CREATE PACKAGE BODY finance AS
  2    FUNCTION compound (
  3      years  IN NUMBER,
  4      amount IN NUMBER,
  5      rate   IN NUMBER
  6    ) RETURN NUMBER
  7    IS
  8    BEGIN
  9      RETURN amount * POWER((rate / 100) + 1, years);
 10    END compound;
 11    -- No pragma in package body
 12  END finance;
 13  /

Package body created.

SQL> DECLARE
  2    interest NUMBER;
  3  BEGIN
  4    SELECT finance.compound(5, 1000, 6)
  5      INTO interest
  6        FROM accounts
  7          WHERE acctno = 12345;
  8  END;
  9  /

PL/SQL procedure successfully completed.

SQL>

内容は次のとおりです。

キーワードTRUSTの使用

PRAGMA RESTRICT REFERENCESにキーワードTRUSTが含まれる場合、プラグマにリストされている制限は適用されませんが、真であると判断できます。

プラグマを使用するPL/SQLコードのセクションから、プラグマを使用しないコードのセクションにあるルーチン(Javaメソッドなど)を起動するときは、起動されるルーチンか起動するルーチンのどちらかについて、PRAGMA RESTRICT REFERENCESTRUSTを指定します。

例7-19例7-20では両方とも、PL/SQLファンクションfがJavaプロシージャjava_sleepを起動します。例7-19では、java_sleepTRUST付きのWNDSになるように宣言されているためこれが可能です。例7-20では、fTRUST付きのWNDSになるように宣言されているためこれが可能です。この場合、fはすべてのルーチンを起動できます。

例7-19 起動される側にTRUSTが指定されたPRAGMA RESTRICT REFERENCES

SQL> CREATE OR REPLACE PACKAGE p IS
  2    PROCEDURE java_sleep (milli_seconds IN NUMBER)
  3      AS LANGUAGE JAVA NAME 'java.lang.Thread.sleep(long)';
  4        PRAGMA RESTRICT_REFERENCES(java_sleep,WNDS,TRUST);
  5
  6    FUNCTION f (n NUMBER) RETURN NUMBER;
  7  END p;
  8  /

Package created.

SQL> CREATE OR REPLACE PACKAGE BODY p IS
  2    FUNCTION f (
  3      n NUMBER
  4    ) RETURN NUMBER
  5    IS
  6    BEGIN
  7      java_sleep(n);
  8    END f;
  9  END p;
 10  /

Package body created.

SQL>

例7-20 起動する側にTRUSTが指定されたPRAGMA RESTRICT REFERENCES

SQL> CREATE OR REPLACE PACKAGE p IS
  2    PROCEDURE java_sleep (milli_seconds IN NUMBER)
  3      AS LANGUAGE JAVA NAME 'java.lang.Thread.sleep(long)';
  4
  5    FUNCTION f (n NUMBER) RETURN NUMBER;
  6      PRAGMA RESTRICT_REFERENCES(f,WNDS,TRUST);
  7  END p;
  8  /

Package created.

SQL> CREATE OR REPLACE PACKAGE BODY p IS
  2    FUNCTION f (
  3      n NUMBER
  4    ) RETURN NUMBER
  5    IS
  6    BEGIN
  7      java_sleep(n);
  8    END f;
  9  END p;
 10  /

Package body created.

SQL>
静的SQL文と動的SQL文の違い

静的なINSERTUPDATEおよびDELETE文は、表の列などのデータベース状態を明示的に読み込まない場合は、RNDSには違反しません。ただし、動的なINSERTUPDATEおよびDELETEの場合は、データベース状態を明示的に読み込むかどうかにかかわらず、常にRNDSに違反します。

次のINSERTは、動的に実行される場合はRNDSに違反しますが、静的に実行される場合はRNDSに違反しません。

INSERT INTO my_table values(3, 'BOB');

次のUPDATEは、my_tableの列名を明示的に読み込むため、静的に実行された場合も動的に実行された場合もRNDSに違反します。

UPDATE my_table SET id=777 WHERE name='BOB';
パッケージPL/SQLファンクションのオーバーロード

PL/SQLでは、パッケージ・ファンクション(スタンドアロン以外)のオーバーロードが可能です。つまり、仮パラメータの数、順序、データ型ファミリなどが異なっていれば、別のファンクションに対して同じ名前を使用できます。ただし、PRAGMA RESTRICT_REFERENCESが適用されるのは、1つのファンクション宣言(最新の宣言)のみです。

例7-21では、プラグマは、2番目のvalidの宣言に適用されます。

例7-21 PRAGMA RESTRICT_REFERENCESを含むパッケージ・ファンクションのオーバーロード

SQL> CREATE OR REPLACE PACKAGE tests AS
  2     FUNCTION valid (x NUMBER) RETURN CHAR;
  3     FUNCTION valid (x DATE) RETURN CHAR;
  4     PRAGMA RESTRICT_REFERENCES (valid, WNDS);
  5   END;
  6  /

Package created.

SQL>

逐次再使用可能PL/SQLパッケージ

通常、PL/SQLパッケージでは、パッケージ内のパッケージ変数およびカーソルの数に応じて、ユーザー・グローバル領域(UGA)のメモリーが消費されます。このメモリーはユーザー数に比例して増加するため、スケーラビリティが制限されます。これを解決するには、プラグマ構文を使用して一部のパッケージにSERIALLY_REUSABLEのマークを付けます。

逐次再利用可能パッケージの場合、パッケージのグローバル・メモリーは各ユーザーのUGAではなく、小さなプールに保持され、複数の異なるユーザーのために再利用されます。したがって、このようなパッケージのグローバル・メモリーは、作業単位内でのみ使用されます。そのため、作業単位の終了時にメモリーはそのプールに解放され、(すべてのグローバル変数の初期化コードの実行後)別のユーザーによって再利用されます。

逐次再利用可能パッケージの作業単位とは、サーバーへのOCIコール、PL/SQLのクライアント・サーバー間RPCコール、PL/SQLのサーバー間RPCコールなどのサーバーへのコールです。

内容は次のとおりです。

パッケージ状態

再利用不可能パッケージ(SERIALLY_REUSABLEのマークが付いていない)の状態は、セッションの存続期間を通じて持続します。パッケージ状態には、グローバル変数やカーソルなどが含まれます。

逐次再利用可能パッケージの状態は、サーバーへのコールの存続期間中のみ持続します。サーバーへの後続のコールでは、逐次再利用パッケージが参照される場合、Oracle Databaseにより逐次再利用可能パッケージの新しいインスタンス化が作成され、すべてのグローバル変数をNULLに、または指定したデフォルト値に初期化します。サーバーへの以前のコール内の逐次再利用可能パッケージ状態に対して行われた変更は参照できません。


注意:

サーバーへのコール時に逐次再利用可能パッケージの新しいインスタンス化が作成されても、Oracle Databaseによってメモリーが割り当てられたり、インスタンス化オブジェクトが構成されるとはかぎりません。Oracle Databaseは、SGAの前回使用されてから最も時間の経過している(LRU)プールにある、このパッケージで使用可能な(割当ておよび構成済の)インスタンス化作業領域を探します。

サーバーへのコールの終了時に、この作業域はLRUプールに戻されます。SGA内にプールがあるのは、同じパッケージに対する要求を持つユーザー間で、作業域を再利用できるようにするためです。


逐次再利用可能パッケージを使用する理由

再利用不可能パッケージの状態はセッションの存続期間を通じて持続するので、セッション全体のUGAメモリーがロックされます。Oracle Officeなどのアプリケーションでは、ログイン・セッションは一般に何日も持続します。アプリケーションでは、特定のパッケージがセッション中の短時間にのみ必要になることがよくあります。またこのようなアプリケーションでは、パッケージの使用後に(セッションの途中に)パッケージ状態が非インスタンス化されるのが理想的です。

SERIALLY_REUSABLEパッケージを使用すると、メモリーをより適切に管理してスケーラビリティを向上させるアプリケーションを設計できます。サーバーへのコール中にのみ管理されるパッケージ状態は、SERIALLY_REUSABLEパッケージ内で取得できます。

逐次再利用可能パッケージの構文

パッケージは、プラグマにより逐次再利用可能のマークが付けられます。プラグマの構文は、次のとおりです。

PRAGMA SERIALLY_REUSABLE;

パッケージ仕様部は、対応するパッケージ本体の有無にかかわらず、逐次再利用可能のマークを付けられます。パッケージに本体がある場合、対応する仕様部に逐次再利用可能プラグマがあると、本体にもそのプラグマが必要です。逐次再利用可能プラグマは、仕様部にそのプラグマがないかぎり、本体に含めることはできません。

逐次再利用可能パッケージのセマンティック

SERIALLY_REUSABLEのマークが付いたパッケージには、次のプロパティがあります。

  • パッケージ変数は、サーバーへのコールに対応した作業境界(OCIコール境界またはサーバーへのPL/SQL RPCコールのいずれか)の中でのみ使用できます。


    注意:

    アプリケーション・プログラマが、誤って前の作業単位で設定されたパッケージ変数を使用した場合、このアプリケーション・プログラムは、正常に実行されない可能性があります。PL/SQLでは、このようなケースはチェックされません。

  • パッケージ・インスタンス化のプールが保持され、作業単位にこのパッケージが必要な場合は、必ずこのインスタンス化の1つが次のように再利用されます。

    • パッケージ変数が再度初期化されます(たとえば、パッケージ変数がデフォルト値の場合、これらの変数は再度初期化されます)。

    • このパッケージ本体の初期化コードが、再実行されます。

  • 作業終了境界で、クリーンアップが行われます。

    • カーソルがオープンされたままの場合、暗黙的にクローズされます。

    • 再利用不可能なセカンダリ・メモリーの一部が解放されます(コレクション変数や長いVARCHAR2のメモリーなど)。

    • このパッケージ・インスタンス化は、このパッケージ用に保持された再利用可能インスタンス化のプールに戻されます。

  • データベース・トリガー、またはSQL文から起動される他のPL/SQLサブプログラムから、逐次再利用可能PL/SQLパッケージにアクセスすることはできません。アクセスしようとすると、Oracle Databaseによりエラーが生成されます。

逐次再利用可能パッケージの例

例7-22で指定される2つのパッケージは、逐次再利用可能かどうかを除いて同じです。どちらのパッケージにも本体はありません。

例7-22 逐次再利用可能パッケージの仕様部

SQL> CREATE OR REPLACE PACKAGE pkg IS
  2    n NUMBER := 5;
  3  END pkg;
  4  /

Package created.

SQL> CREATE OR REPLACE PACKAGE sr_pkg IS
  2    PRAGMA SERIALLY_REUSABLE;
  3    n NUMBER := 5;
  4  END sr_pkg;
  5  /

Package created.

SQL> BEGIN
  2    pkg.n := 10;
  3    sr_pkg.n := 10;
  4  END;
  5  /

PL/SQL procedure successfully completed.

SQL> BEGIN
  2    DBMS_OUTPUT.PUT_LINE('pkg.n: ' || pkg.n);
  3    DBMS_OUTPUT.PUT_LINE('sr_pkg.n: ' || sr_pkg.n);
  4  END;
  5  /
pkg.n: 10
sr_pkg.n: 5

PL/SQL procedure successfully completed.

SQL>

例7-23のパッケージには本体があります。このパッケージ仕様部は逐次利用可能であるため、パッケージ本体も逐次利用可能であることが必要です。

例7-23 逐次利用可能なパッケージ仕様部およびパッケージ本体

SQL> CREATE OR REPLACE PACKAGE sr_pkg IS
  2    PRAGMA SERIALLY_REUSABLE;
  3    TYPE str_table_type IS TABLE OF VARCHAR2(200) INDEX BY PLS_INTEGER;
  4    num      NUMBER        := 10;
  5    str      VARCHAR2(200) := 'default-init-str';
  6    str_tab  STR_TABLE_TYPE;
  7
  8    PROCEDURE print_pkg;
  9
 10    PROCEDURE init_and_print_pkg (
 11      n NUMBER,
 12      v VARCHAR2
 13    );
 14  END sr_pkg;
 15  /

Package created.

SQL> CREATE OR REPLACE PACKAGE BODY sr_pkg IS
  2    PRAGMA SERIALLY_REUSABLE;
  3
  4     PROCEDURE print_pkg IS
  5       num      NUMBER;
  6       str      VARCHAR2(200);
  7       str_tab  STR_TABLE_TYPE;
  8     BEGIN
  9       DBMS_OUTPUT.PUT_LINE('num: ' || sr_pkg.num);
 10       DBMS_OUTPUT.PUT_LINE('str: ' || sr_pkg.str);
 11       DBMS_OUTPUT.PUT_LINE
 12         ('number of table elements: ' || sr_pkg.str_tab.count);
 13       FOR i IN 1..sr_pkg.str_tab.count LOOP
 14         DBMS_OUTPUT.PUT_LINE(sr_pkg.str_tab(i));
 15       END LOOP;
 16     END print_pkg;
 17
 18     PROCEDURE init_and_print_pkg (
 19      n NUMBER,
 20      v VARCHAR2
 21     ) IS
 22     BEGIN
 23       sr_pkg.num := n;
 24       sr_pkg.str := v;
 25
 26       FOR i IN 1..n LOOP
 27         sr_pkg.str_tab(i) := v || ' ' || i;
 28       END LOOP;
 29
 30       Print_pkg;
 31     END  init_and_print_pkg;
 32   END sr_pkg;
 33  /

Package body created.

SQL> BEGIN
  2     DBMS_OUTPUT.PUT_LINE('Initializing and printing package state ...');
  3     sr_pkg.init_and_print_pkg(4, 'abracadabra');
  4     DBMS_OUTPUT.PUT_LINE('Printing package state in the same CALL ...');
  5     sr_pkg.print_pkg;
  6  END;
  7  /
Initializing and printing package state ...
num: 4
str: abracadabra
number of table elements: 4
abracadabra 1
abracadabra 2
abracadabra 3
abracadabra 4
Printing package state in the same CALL ...
num: 4
str: abracadabra
number of table elements: 4
abracadabra 1
abracadabra 2
abracadabra 3
abracadabra 4

PL/SQL procedure successfully completed.

SQL>

逐次利用可能なパッケージのオープン・カーソルは、サーバー・コールが終了すると自動的にクローズされます。新しいサーバー・コールで再オープンする必要があります。例7-24に示すように、サーバー・コールはサブプログラム・コールとは異なる場合があります。

例7-24 コール境界での逐次再利用可能パッケージのオープン・カーソル

SQL> DROP TABLE people;
DROP TABLE people
           *
ERROR at line 1:
ORA-00942: table or view does not exist

SQL> CREATE TABLE people (name VARCHAR2(20));

Table created.

SQL> INSERT INTO people VALUES ('John Smith');

1 row created.

SQL> INSERT INTO people VALUES ('Mary Jones');

1 row created.

SQL> INSERT INTO people VALUES ('Joe Brown');

1 row created.

SQL> INSERT INTO people VALUES ('Jane White');

1 row created.

SQL> CREATE OR REPLACE PACKAGE sr_pkg IS
  2    PRAGMA SERIALLY_REUSABLE;
  3    CURSOR c IS SELECT name FROM people;
  4  END sr_pkg;
  5  /

Package created.

SQL> CREATE OR REPLACE PROCEDURE fetch_from_cursor IS
  2    name VARCHAR2(200);
  3  BEGIN
  4     IF sr_pkg.c%ISOPEN THEN
  5       DBMS_OUTPUT.PUT_LINE('Cursor is already open.');
  6     ELSE
  7       DBMS_OUTPUT.PUT_LINE('Cursor is closed; opening now.');
  8       OPEN sr_pkg.c;
  9     END IF;
 10
 11     FETCH sr_pkg.c INTO name;
 12     DBMS_OUTPUT.PUT_LINE('Fetched: ' || name);
 13
 14     FETCH sr_pkg.c INTO name;
 15     DBMS_OUTPUT.PUT_LINE('Fetched: ' || name);
 16  END fetch_from_cursor;
 17  /

Procedure created.

SQL> -- First call to server:
SQL> BEGIN
  2    fetch_from_cursor;
  3    fetch_from_cursor;
  4  END;
  5  /
Cursor is closed; opening now.
Fetched: John Smith
Fetched: Mary Jones
Cursor is already open.
Fetched: Joe Brown
Fetched: Jane White

PL/SQL procedure successfully completed.

SQL> -- New call to server:
SQL> BEGIN
  2    fetch_from_cursor;
  3    fetch_from_cursor;
  4  END;
  5  /
Cursor is closed; opening now.
Fetched: John Smith
Fetched: Mary Jones
Cursor is already open.
Fetched: Joe Brown
Fetched: Jane White

PL/SQL procedure successfully completed.

SQL>

ファンクションからの大量のデータの戻し

データ・ウェアハウス環境では、大量のデータを変換するためにPL/SQLファンクションを使用します。データは、異なるファンクションによる一連の変換を経由して渡されます。PL/SQL表ファンクションを使用すると、このような変換を、かなりのメモリー・オーバーヘッドを必要としたり、各変換の間でデータを表に格納する必要なく実行できます。これらのファンクションは、複数の行を受け入れて戻すことが可能で、行は、一度ではなく準備できた順に戻すことができます。さらにパラレル化も可能です。


関連項目:

パイプライン・テーブル・ファンクションを使用して複数の変換を実行する方法の詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

独自の集計関数のコード化

一連の行を分析して結果値を計算するために、次のようにSUMなどの組込み集計と同様に機能する集計関数をコード化することができます。


関連項目:

ユーザー定義の集計関数の詳細は、『Oracle Databaseデータ・カートリッジ開発者ガイド』を参照してください。