【Oracle】ストアドプロシージャ・ファンクションの作成完全ガイド|CREATE PROCEDURE・IN/OUT・例外処理・実務パターン

【Oracle】ストアドプロシージャ・ファンクションの作成完全ガイド|CREATE PROCEDURE・IN/OUT・例外処理・実務パターン Oracle

Oracleで繰り返し使うSQL処理やビジネスロジックをストアドプロシージャファンクションにまとめておくと、コードの再利用性・保守性・セキュリティが大幅に向上します。本記事ではCREATE文の基本構文から、パラメータの使い方、例外処理、実務で使う設計パターンまで体系的に解説します。

この記事でわかること

  • プロシージャとファンクションの違いと使い分け
  • CREATE PROCEDURE の基本構文と IN / OUT / IN OUT パラメータ
  • CREATE FUNCTION の基本構文と RETURN 句
  • 例外処理(EXCEPTION セクション)の書き方
  • プロシージャ・ファンクションの実行方法
  • CREATE OR REPLACE / DROP / コンパイルエラーの確認
  • 実務で使う設計パターン
スポンサーリンク

プロシージャとファンクションの違い

比較項目 プロシージャ(PROCEDURE) ファンクション(FUNCTION)
戻り値 なし(OUT パラメータで返す) あり(RETURN で1つの値を返す)
SQL文内での呼び出し 不可 可能(SELECT/WHERE/ORDER BY等で使える)
呼び出し方法 EXECUTE proc_name / CALL proc_name SELECT文内、PL/SQLブロック内
主な用途 DML操作・バッチ処理・業務ロジック 値の計算・変換・判定
使い分けの指針
「値を1つ返して SQL の中で使いたい」ならファンクション、「一連の処理を実行したい」ならプロシージャを選んでください。

プロシージャの作成(CREATE PROCEDURE)

最もシンプルなプロシージャ

パラメータなしのプロシージャ
CREATE OR REPLACE PROCEDURE proc_hello
IS
BEGIN
    DBMS_OUTPUT.PUT_LINE('Hello, Oracle!');
END;
/

IN パラメータ(入力)

IN パラメータ ── 値を受け取るプロシージャ
CREATE OR REPLACE PROCEDURE proc_update_salary (
    p_employee_id  IN NUMBER,      -- 入力パラメータ(デフォルトは IN)
    p_new_salary   IN NUMBER
)
IS
BEGIN
    UPDATE employees
    SET salary = p_new_salary
    WHERE employee_id = p_employee_id;

    IF SQL%ROWCOUNT = 0 THEN
        RAISE_APPLICATION_ERROR(-20001,
            '社員ID ' || p_employee_id || ' が見つかりません');
    END IF;

    COMMIT;
END;
/

OUT パラメータ(出力)

OUT パラメータ ── 結果を呼び出し元に返すプロシージャ
CREATE OR REPLACE PROCEDURE proc_get_employee_info (
    p_employee_id  IN  NUMBER,
    p_name         OUT VARCHAR2,    -- 出力パラメータ
    p_salary       OUT NUMBER
)
IS
BEGIN
    SELECT name, salary
    INTO p_name, p_salary
    FROM employees
    WHERE employee_id = p_employee_id;
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        p_name   := NULL;
        p_salary := NULL;
END;
/

IN OUT パラメータ(入出力)

IN OUT パラメータ ── 値を受け取り、結果を上書きして返す
CREATE OR REPLACE PROCEDURE proc_apply_tax (
    p_amount  IN OUT NUMBER    -- 税抜金額を受け取り、税込金額で上書きして返す
)
IS
    c_tax_rate CONSTANT NUMBER := 0.10;  -- 消費税率 10%
BEGIN
    p_amount := p_amount * (1 + c_tax_rate);
END;
/
モード 呼び出し側で指定する値 プロシージャ内での読み書き 呼び出し元への反映
IN(デフォルト) 定数・変数・式 読み取りのみ 反映しない
OUT 変数のみ 書き込みのみ(初期値はNULL) 反映する
IN OUT 変数のみ 読み書き可能 反映する

ファンクションの作成(CREATE FUNCTION)

ファンクション ── 値を1つ返す
CREATE OR REPLACE FUNCTION fn_get_dept_name (
    p_department_id  IN NUMBER
) RETURN VARCHAR2
IS
    v_dept_name VARCHAR2(100);
BEGIN
    SELECT department_name
    INTO v_dept_name
    FROM departments
    WHERE department_id = p_department_id;

    RETURN v_dept_name;
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        RETURN NULL;
END;
/
ファンクションを SELECT 文で使う
-- SQL文の中でファンクションを呼び出せる
SELECT
    employee_id,
    name,
    salary,
    fn_get_dept_name(department_id) AS dept_name
FROM employees
WHERE department_id = 10;
ファンクション ── 計算ロジックを共通化する
CREATE OR REPLACE FUNCTION fn_calc_age (
    p_birth_date  IN DATE
) RETURN NUMBER
IS
BEGIN
    RETURN TRUNC(MONTHS_BETWEEN(SYSDATE, p_birth_date) / 12);
END;
/

-- 使用例
SELECT name, birth_date, fn_calc_age(birth_date) AS age
FROM employees;
ファンクション内での DML に注意
SELECT文内で呼び出すファンクションに INSERT/UPDATE/DELETE を含めると ORA-14551 が発生します。DMLを含むファンクションは PL/SQL ブロック内でのみ呼び出すか、PRAGMA AUTONOMOUS_TRANSACTION を使う必要がありますが、後者は慎重に検討してください。

例外処理(EXCEPTION セクション)

プロシージャ・ファンクション内でエラーが発生した場合の処理を EXCEPTION セクションに書きます。

例外処理の基本パターン
CREATE OR REPLACE PROCEDURE proc_transfer_salary (
    p_from_id  IN NUMBER,
    p_to_id    IN NUMBER,
    p_amount   IN NUMBER
)
IS
BEGIN
    -- 送金元から減算
    UPDATE employees SET salary = salary - p_amount
    WHERE employee_id = p_from_id;

    IF SQL%ROWCOUNT = 0 THEN
        RAISE_APPLICATION_ERROR(-20001, '送金元の社員が見つかりません');
    END IF;

    -- 送金先に加算
    UPDATE employees SET salary = salary + p_amount
    WHERE employee_id = p_to_id;

    IF SQL%ROWCOUNT = 0 THEN
        RAISE_APPLICATION_ERROR(-20002, '送金先の社員が見つかりません');
    END IF;

    COMMIT;
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        DBMS_OUTPUT.PUT_LINE('エラー: ' || SQLERRM);
        RAISE;  -- エラーを呼び出し元に再送出
END;
/
定義済み例外 ORAエラー 発生条件
NO_DATA_FOUND ORA-01403 SELECT INTO で行が見つからない
TOO_MANY_ROWS ORA-01422 SELECT INTO で複数行が返された
DUP_VAL_ON_INDEX ORA-00001 一意制約違反
ZERO_DIVIDE ORA-01476 ゼロ除算
VALUE_ERROR ORA-06502 サイズ超過・型変換エラー
OTHERS 全て 上記以外の全てのエラー

プロシージャ・ファンクションの実行方法

プロシージャの実行
-- SQL*Plus / SQL Developer から実行
EXECUTE proc_update_salary(1001, 450000);
-- または
CALL proc_update_salary(1001, 450000);

-- PL/SQL ブロック内で実行
BEGIN
    proc_update_salary(1001, 450000);
END;
/

-- OUT パラメータがあるプロシージャの実行
DECLARE
    v_name   VARCHAR2(100);
    v_salary NUMBER;
BEGIN
    proc_get_employee_info(1001, v_name, v_salary);
    DBMS_OUTPUT.PUT_LINE(v_name || ' : ' || v_salary);
END;
/
ファンクションの実行
-- SELECT 文内で実行
SELECT fn_get_dept_name(10) AS dept_name FROM dual;

-- PL/SQL ブロック内で実行
DECLARE
    v_name VARCHAR2(100);
BEGIN
    v_name := fn_get_dept_name(10);
    DBMS_OUTPUT.PUT_LINE(v_name);
END;
/

プロシージャ・ファンクションの管理

削除(DROP)

プロシージャ・ファンクションの削除
DROP PROCEDURE proc_hello;
DROP FUNCTION fn_get_dept_name;

コンパイルエラーの確認

コンパイルエラーを確認する
-- 最後にコンパイルしたオブジェクトのエラーを表示(SQL*Plus)
SHOW ERRORS;

-- 特定のプロシージャのエラーを表示
SHOW ERRORS PROCEDURE proc_update_salary;

-- SQLでエラー一覧を取得する
SELECT line, position, text
FROM user_errors
WHERE name = 'PROC_UPDATE_SALARY'
  AND type = 'PROCEDURE'
ORDER BY line, position;

ソースコードの確認

プロシージャ・ファンクションのソースを確認する
SELECT line, text
FROM user_source
WHERE name = 'PROC_UPDATE_SALARY'
  AND type = 'PROCEDURE'
ORDER BY line;

プロシージャやファンクションの一覧取得・依存関係の調査には USER_PROCEDURESUSER_DEPENDENCIES ビューが活用できます。

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

パラメータにデフォルト値を設定する
CREATE OR REPLACE PROCEDURE proc_search_employees (
    p_department_id  IN NUMBER   DEFAULT NULL,   -- 省略可能(全部門対象)
    p_min_salary     IN NUMBER   DEFAULT 0       -- 省略可能(最低給与なし)
)
IS
BEGIN
    FOR r IN (
        SELECT employee_id, name, salary, department_id
        FROM employees
        WHERE (p_department_id IS NULL OR department_id = p_department_id)
          AND salary >= p_min_salary
        ORDER BY salary DESC
    ) LOOP
        DBMS_OUTPUT.PUT_LINE(r.name || ' : ' || r.salary);
    END LOOP;
END;
/

-- 呼び出し方(名前付きパラメータ指定が便利)
BEGIN
    proc_search_employees(p_min_salary => 400000);  -- 部門指定なし、最低給与40万以上
END;
/

実務パターン:ログ出力付きバッチプロシージャ

ログテーブルへの記録付きバッチ処理
CREATE OR REPLACE PROCEDURE proc_monthly_aggregate (
    p_target_month  IN VARCHAR2    -- 'YYYY-MM' 形式
)
IS
    v_start_time TIMESTAMP := SYSTIMESTAMP;
    v_count      NUMBER := 0;
BEGIN
    -- 集計処理
    INSERT INTO monthly_summary (yr_month, dept_id, total_sales)
    SELECT p_target_month, department_id, SUM(amount)
    FROM sales
    WHERE TO_CHAR(sale_date, 'YYYY-MM') = p_target_month
    GROUP BY department_id;

    v_count := SQL%ROWCOUNT;
    COMMIT;

    -- ログ記録
    INSERT INTO batch_log (proc_name, target_month, row_count, started_at, finished_at, status)
    VALUES ('proc_monthly_aggregate', p_target_month, v_count, v_start_time, SYSTIMESTAMP, 'SUCCESS');
    COMMIT;

EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        INSERT INTO batch_log (proc_name, target_month, row_count, started_at, finished_at, status, error_msg)
        VALUES ('proc_monthly_aggregate', p_target_month, 0, v_start_time, SYSTIMESTAMP, 'ERROR', SQLERRM);
        COMMIT;
        RAISE;
END;
/

まとめ

ストアドプロシージャとファンクションは Oracle PL/SQL の中核機能です。適切に使い分けることで、コードの再利用性と保守性が大きく向上します。

  • プロシージャ:戻り値なし。DML操作やバッチ処理に使う。OUT パラメータで複数の値を返せる
  • ファンクション:RETURN で1つの値を返す。SELECT 文内で呼び出せるのが最大の利点
  • パラメータモードは IN(入力・デフォルト)、OUT(出力)、IN OUT(入出力)の3種類
  • EXCEPTION セクションで NO_DATA_FOUND / DUP_VAL_ON_INDEX / OTHERS 等のエラーを捕捉する
  • CREATE OR REPLACE で既存のプロシージャを上書き作成できる
  • コンパイルエラーは SHOW ERRORS または USER_ERRORS ビューで確認する
  • 名前付きパラメータ(p_name => 値)とデフォルト値を組み合わせると柔軟な呼び出しが可能