8.

Oracle PL/SQL パッケージ 完全ガイド(仕様部 / 本体 / グローバル変数 / 利点と設計指針)

編集
この記事の要点
  • パッケージ (PACKAGE) は Oracle PL/SQL の関連プロシージャ / ファンクション / 変数 / カーソル / 例外をひとつの名前空間にまとめる仕組み
  • 仕様部 (PACKAGE) と本体 (PACKAGE BODY) の 2 段構成 — 仕様部は公開インターフェース、本体は実装
  • 主な利点: カプセル化 / 名前空間 / セッション持続のグローバル変数 / パフォーマンス向上 (初回コール時にコンパイル済みコードをキャッシュ)
  • 本体に書いたプロシージャはプライベート扱い、仕様部に宣言したもののみ外部から呼べる
  • 初期化処理は BEGIN ... END ブロックで — セッション内で初回参照時に 1 度だけ実行

パッケージとは

パッケージ (PACKAGE) は Oracle 独自の PL/SQL オブジェクトで、関連するプロシージャ・ファンクション・変数・型・カーソル・例外を 1 つの単位にまとめたものです。Java のクラスや C# のクラス、モジュールに近い概念。

業務システムでは「顧客処理パッケージ」「受注処理パッケージ」のように業務単位でまとめるのが定石で、肥大化したスタンドアロンプロシージャを整理する強力な手段になります。

パッケージの構成 — 2 段構成

区分役割
パッケージ仕様部 (PACKAGE)外部に公開するインターフェース。関数シグネチャ、定数、型を宣言
パッケージ本体 (PACKAGE BODY)仕様部で宣言したプロシージャ / ファンクションの実装と、プライベートメンバー

基本構文 — 仕様部

CREATE OR REPLACE PACKAGE pkg_customer AS

    -- 公開定数
    c_default_status CONSTANT VARCHAR2(10) := 'ACTIVE';

    -- 公開型
    TYPE customer_rec IS RECORD (
        customer_id   NUMBER,
        customer_name VARCHAR2(100),
        email         VARCHAR2(255)
    );

    -- 公開プロシージャ / ファンクション
    PROCEDURE register_customer(
        p_name  IN  VARCHAR2,
        p_email IN  VARCHAR2,
        p_id    OUT NUMBER
    );

    FUNCTION find_customer(p_id IN NUMBER) RETURN customer_rec;

    FUNCTION count_active_customers RETURN NUMBER;

END pkg_customer;
/

基本構文 — 本体 (PACKAGE BODY)

CREATE OR REPLACE PACKAGE BODY pkg_customer AS

    -- プライベート変数 (仕様部に宣言なし -> 外部から見えない)
    g_last_inserted_id NUMBER;

    -- プライベートプロシージャ (内部ヘルパー)
    PROCEDURE log_action(p_action VARCHAR2) IS
    BEGIN
        INSERT INTO action_log(action, log_time)
        VALUES (p_action, SYSDATE);
    END log_action;

    -- 公開プロシージャの実装
    PROCEDURE register_customer(
        p_name  IN  VARCHAR2,
        p_email IN  VARCHAR2,
        p_id    OUT NUMBER
    ) IS
    BEGIN
        INSERT INTO customers(name, email, status)
        VALUES (p_name, p_email, c_default_status)
        RETURNING customer_id INTO p_id;

        g_last_inserted_id := p_id;
        log_action('REGISTER: ' || p_name);
    END register_customer;

    FUNCTION find_customer(p_id IN NUMBER) RETURN customer_rec IS
        v_rec customer_rec;
    BEGIN
        SELECT customer_id, name, email
          INTO v_rec
          FROM customers
         WHERE customer_id = p_id;
        RETURN v_rec;
    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            RETURN NULL;
    END find_customer;

    FUNCTION count_active_customers RETURN NUMBER IS
        v_count NUMBER;
    BEGIN
        SELECT COUNT(*) INTO v_count
          FROM customers WHERE status = c_default_status;
        RETURN v_count;
    END count_active_customers;

    -- パッケージ初期化ブロック (セッション内で初回参照時に 1 度だけ実行)
BEGIN
    g_last_inserted_id := 0;
    log_action('PACKAGE INITIALIZED');
END pkg_customer;
/

パッケージの呼び出し

-- 名前空間付きで呼び出し
DECLARE
    v_id  NUMBER;
    v_cnt NUMBER;
    v_rec pkg_customer.customer_rec;
BEGIN
    pkg_customer.register_customer('山田太郎', 'yamada@example.com', v_id);
    DBMS_OUTPUT.PUT_LINE('登録 ID: ' || v_id);

    v_cnt := pkg_customer.count_active_customers;
    DBMS_OUTPUT.PUT_LINE('アクティブ件数: ' || v_cnt);

    v_rec := pkg_customer.find_customer(v_id);
    DBMS_OUTPUT.PUT_LINE('名前: ' || v_rec.customer_name);
END;
/

パッケージのメリット

メリット詳細
カプセル化本体の実装変更が仕様部に影響しないなら、呼び出し側の再コンパイル不要
名前空間pkg_customer.findpkg_order.find のように同名関数を共存させられる
セッション変数パッケージのグローバル変数はセッション内で値を保持 (擬似的なクラス変数)
パフォーマンス初回参照時にパッケージ全体がメモリにロードされる — 関連処理の連続実行が高速
オーバーロード同名で引数の異なる関数を複数定義可
権限管理が楽パッケージ単位で EXECUTE 権限を付与

パッケージのプライベートとパブリック

宣言場所公開範囲
仕様部に宣言 + 本体に実装パブリック (外部から呼べる)
本体のみに宣言 + 実装プライベート (パッケージ内部のみ)

パッケージ初期化ブロック

パッケージ本体の末尾に BEGIN ... END; を書くと、そのパッケージがセッション内で初めて参照されたときに 1 回だけ実行されます。設定読み込みや初期データ取得に便利。

CREATE OR REPLACE PACKAGE BODY pkg_config AS
    g_app_name    VARCHAR2(100);
    g_environment VARCHAR2(20);

    FUNCTION app_name RETURN VARCHAR2 IS
    BEGIN RETURN g_app_name; END;

    FUNCTION environment RETURN VARCHAR2 IS
    BEGIN RETURN g_environment; END;

BEGIN
    -- セッション開始後、最初に pkg_config が参照されたタイミングで実行
    SELECT name, env INTO g_app_name, g_environment
      FROM app_config WHERE active = 'Y' AND ROWNUM = 1;
END pkg_config;
/

オーバーロード

CREATE OR REPLACE PACKAGE pkg_util AS
    FUNCTION fmt(p_num IN NUMBER)     RETURN VARCHAR2;
    FUNCTION fmt(p_date IN DATE)      RETURN VARCHAR2;
    FUNCTION fmt(p_text IN VARCHAR2)  RETURN VARCHAR2;
END pkg_util;
/

CREATE OR REPLACE PACKAGE BODY pkg_util AS
    FUNCTION fmt(p_num IN NUMBER) RETURN VARCHAR2 IS
    BEGIN RETURN TO_CHAR(p_num, 'FM999,999,990.00'); END;

    FUNCTION fmt(p_date IN DATE) RETURN VARCHAR2 IS
    BEGIN RETURN TO_CHAR(p_date, 'YYYY-MM-DD HH24:MI:SS'); END;

    FUNCTION fmt(p_text IN VARCHAR2) RETURN VARCHAR2 IS
    BEGIN RETURN '[' || p_text || ']'; END;
END pkg_util;
/

パッケージの管理

-- パッケージ一覧
SELECT object_name, status
  FROM user_objects
 WHERE object_type IN ('PACKAGE','PACKAGE BODY');

-- 仕様部 / 本体のソース取得
SELECT text
  FROM user_source
 WHERE name = 'PKG_CUSTOMER' AND type = 'PACKAGE'
 ORDER BY line;

-- パッケージ削除
DROP PACKAGE pkg_customer;        -- 仕様部 + 本体 ともに削除
DROP PACKAGE BODY pkg_customer;   -- 本体のみ

設計のコツ

  • 業務単位でパッケージを切る (顧客 / 受注 / 在庫など)
  • 仕様部は安定させる — 本体だけ変更すれば呼び出し側の再コンパイル不要
  • セッション変数は濫用しない — 接続プール環境ではセッション再利用で状態が漏れる
  • プライベートヘルパーは積極的に活用 — 仕様部を肥大化させない
  • 定数 (CONSTANT) を仕様部に集約してマジックナンバーを排除

FAQ

Q: パッケージとプロシージャ単独はどちらを使うべき?
A: パッケージ推奨。名前空間、初期化、グローバル変数、依存関係解決の安定性などメリットが大きい。スタンドアロンプロシージャは依存先の変更でステータスが INVALID になりやすい。

Q: パッケージのグローバル変数はトランザクション単位?
A: いいえ、セッション単位で保持される。COMMIT / ROLLBACK では消えない。DBMS_SESSION.RESET_PACKAGE で明示的にリセット可。

Q: 本体だけ変更したのに呼び出し側がエラーになる
A: 仕様部のタイムスタンプが変わると呼び出し側が INVALID になる仕様。ALTER ... COMPILE で再コンパイルするか、シグネチャ依存性を使う設定にしておく。

関連

  • マテリアライズド・ビュー — Oracle のもうひとつの強力な機能
  • プロシージャ / ファンクション — 単体の PL/SQL オブジェクト
  • カーソル / 例外処理 — PL/SQL の基本構文
編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 表(テーブル)
  2. 索引(インデックス)
  3. ビュー
  4. 制約
  5. 順序(シーケンス)
  6. シノニム
  7. トリガー
  8. パッケージ
  9. ストアド・ファンクション
  10. ストアド・プロシージャ
  11. ユーザー(USER)

最近更新/作成されたページ