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 の中で使いたい」ならファンクション、「一連の処理を実行したい」ならプロシージャを選んでください。
「値を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 ブロック内でのみ呼び出すか、
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_PROCEDURES・USER_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 => 値)とデフォルト値を組み合わせると柔軟な呼び出しが可能
