この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:3
ページ更新者:guest
更新日時:2026-06-11 07:07:02

タイトル: パッケージ
SEOタイトル: 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 の基本構文