PL/SQL でプロシージャやファンクションを定義するとき、パラメータには IN・OUT・IN OUT の3つのモードがあります。モードを正しく指定しないと、値が返ってこない・意図しない値が書き変わるなどのバグが発生します。
この記事では、3つのモードの基本から DEFAULT値・NOCOPYヒント・ファンクションとの使い分けまで、実践コード付きで完全解説します。
この記事でわかること
- IN・OUT・IN OUT の役割と違い(値渡し vs 参照渡し)
- 各モードの具体的なコード例
- DEFAULT 値でオプションパラメータを作る方法
- NOCOPY ヒントで大きな型を効率的に渡す方法
- ファンクションとプロシージャのパラメータの使い分け
- よくある間違いとその対処法
IN・OUT・IN OUT の違い一覧
| モード | 値の流れ | 渡し方 | プロシージャ内での操作 | 主な用途 |
|---|---|---|---|---|
| IN | 呼び出し元 → プロシージャ | 値渡し(コピー) | 読み取り専用 | 検索条件・計算の入力値 |
| OUT | プロシージャ → 呼び出し元 | 参照渡し | 書き込み専用(初期値はNULL) | 処理結果を返す |
| IN OUT | 双方向 | 参照渡し | 読み書き両方可能 | 値を受け取り変換して返す |
デフォルトモードは IN
モードを省略した場合、自動的に
モードを省略した場合、自動的に
IN として扱われます。CREATE PROCEDURE proc(p_id NUMBER) と CREATE PROCEDURE proc(p_id IN NUMBER) は同じ意味です。IN パラメータ:値を受け取る(読み取り専用)
最も基本的なモードです。呼び出し元から値を受け取りますが、プロシージャ内で値を変更することはできません。値渡しなので呼び出し元の変数は変わりません。
-- IN パラメータの例
CREATE OR REPLACE PROCEDURE greet_user(
p_name IN VARCHAR2 -- IN は省略可
) AS
BEGIN
DBMS_OUTPUT.PUT_LINE('こんにちは、' || p_name || 'さん');
-- p_name := '別の名前'; ← エラー!IN は代入不可
END;
/
-- 呼び出し方
BEGIN
greet_user('田中');
-- → こんにちは、田中さん
END;
/
IN パラメータに DEFAULT 値を設定する
DEFAULT キーワードでデフォルト値を設定すると、引数を省略して呼び出せる オプションパラメータ になります。
CREATE OR REPLACE PROCEDURE log_message(
p_message IN VARCHAR2,
p_severity IN VARCHAR2 DEFAULT 'INFO' -- 省略可能
) AS
BEGIN
DBMS_OUTPUT.PUT_LINE('['||p_severity||'] '||p_message);
END;
/
-- severity を省略して呼び出し
BEGIN
log_message('処理が完了しました');
-- → [INFO] 処理が完了しました
log_message('エラーが発生しました', 'ERROR');
-- → [ERROR] エラーが発生しました
END;
/
OUT パラメータ:結果を返す(書き込み専用)
プロシージャ内で計算した結果を呼び出し元に返すために使います。プロシージャ開始時の値は NULLになるため、読み取りは行わず必ず値を設定して返します。
CREATE OR REPLACE PROCEDURE calc_tax(
p_price IN NUMBER,
p_tax OUT NUMBER,
p_total OUT NUMBER
) AS
BEGIN
-- p_tax の入力値は使えない(NULLとして扱われる)
p_tax := ROUND(p_price * 0.1);
p_total := p_price + p_tax;
END;
/
-- 呼び出し方
DECLARE
v_tax NUMBER;
v_total NUMBER;
BEGIN
calc_tax(1000, v_tax, v_total);
DBMS_OUTPUT.PUT_LINE('税額: ' || v_tax); -- → 税額: 100
DBMS_OUTPUT.PUT_LINE('合計: ' || v_total); -- → 合計: 1100
END;
/
OUT パラメータに定数・式を渡すことはできない
calc_tax(1000, 0, 0) のように定数を OUT 引数に渡すとエラーになります。OUT は呼び出し元の変数を渡す必要があります。IN OUT パラメータ:受け取って変換して返す
呼び出し元の値を受け取り、変換・加工して同じ変数に書き戻す場合に使います。呼び出し元の変数が直接書き変わります。
-- 文字列を大文字に変換して上書きするプロシージャ
CREATE OR REPLACE PROCEDURE to_upper_case(
p_str IN OUT VARCHAR2
) AS
BEGIN
p_str := UPPER(p_str);
END;
/
DECLARE
v_str VARCHAR2(100) := 'hello world';
BEGIN
DBMS_OUTPUT.PUT_LINE(v_str); -- → hello world
to_upper_case(v_str);
DBMS_OUTPUT.PUT_LINE(v_str); -- → HELLO WORLD
END;
/
IN OUT の典型的な使いどころ
- 数値の丸め・変換(価格の税込変換など)
- 文字列の正規化(トリム・大文字変換など)
- カウンタや累積値の更新
- コレクション型(VARRAY/TABLE)の要素追加
NOCOPY ヒント:大きな型を効率的に渡す
OUT・IN OUT は参照渡しですが、Oracle は内部的に一時コピーを作ることがあります。CLOB・BLOB・大きなコレクションを渡す場合、NOCOPY ヒントでコピーを省略してパフォーマンスを改善できます。
CREATE OR REPLACE PROCEDURE process_data(
p_data IN OUT NOCOPY CLOB -- コピーを省略
) AS
BEGIN
-- 大きなCLOBを処理
DBMS_LOB.WRITEAPPEND(p_data, 10, '追加データ');
END;
/
NOCOPY の注意点
NOCOPY を使うと、プロシージャが例外で終了しても呼び出し元の変数が変更されてしまう可能性があります。ROLLBACK で元に戻せない場合があるため、例外処理を適切に実装してから使用してください。ファンクション vs プロシージャのパラメータ使い分け
ファンクションは戻り値で1つの値を返せます。複数の値を返す必要がある場合はプロシージャの OUT パラメータを使います。
| ファンクション | プロシージャ(OUT使用) | |
|---|---|---|
| 返す値の数 | 1つ(RETURN) | 複数可(OUT を複数定義) |
| SQL からの呼び出し | 可能 | 不可 |
| エラー処理 | RETURN で即座に返せる | OUT に設定して EXIT |
| 主な用途 | 値の計算・変換 | 複数結果の返却・DML 処理 |
-- ファンクション版(1つの値を返す場合)
CREATE OR REPLACE FUNCTION calc_tax_fn(
p_price IN NUMBER
) RETURN NUMBER AS
BEGIN
RETURN ROUND(p_price * 0.1);
END;
/
-- SQL から直接使用できる
SELECT calc_tax_fn(1000) FROM DUAL; -- → 100
よくある間違いと対処法
Q. OUT パラメータで「ORA-06502: PL/SQL: 数値または値のエラー」が出ます。
A. OUT パラメータに値を設定せずに終了した場合や、呼び出し元の変数が小さすぎる場合に発生します。プロシージャ内で OUT パラメータに必ず値を設定しているか確認してください。また、呼び出し元の変数の型・サイズがプロシージャの定義と一致しているか確認してください。
Q. IN パラメータなのにプロシージャ内で代入しようとするとエラーになります。
A. IN パラメータは読み取り専用のため、プロシージャ内で代入(
p_name := '別の値')はできません。「PLS-00363: 式 'P_NAME' を代入のターゲットにすることはできません」というエラーが出ます。値を変更する必要がある場合は IN OUT に変更するか、ローカル変数にコピーして使ってください。Q. IN OUT パラメータに定数を渡したらエラーになります。
A. IN OUT は参照渡しのため、変数(
v_num など)を渡す必要があります。proc(100) のように定数・リテラルを直接渡すと「PLS-00457: 式は有効な左辺値でなければなりません」というエラーになります。呼び出し前に変数に代入してから渡してください。Q. OUT パラメータが複数ある場合、呼び出し方は?
A. カンマ区切りで変数を渡します。
calc_tax(1000, v_tax, v_total) のように、定義した順番に引数を渡してください。キーワード記法(名前付き引数)を使う場合は calc_tax(p_price => 1000, p_tax => v_tax, p_total => v_total) のように書けば順不同で渡せます。Q. DEFAULT 値を設定した引数をスキップして後ろの引数を指定できますか?
A. 位置記法(順番で渡す方法)ではスキップできませんが、キーワード記法を使えばスキップできます。
log_message(p_message => 'メッセージ') のように名前付きで渡すと、デフォルト値のある引数を省略できます。まとめ
PL/SQL パラメータモードのまとめ
- IN:呼び出し元から値を受け取る(読み取り専用・デフォルト)
- OUT:計算結果を呼び出し元に返す(書き込み専用・初期値NULL)
- IN OUT:値を受け取り変換して返す(読み書き可・呼び出し元変数を直接変更)
- 複数の値を返す → プロシージャ + 複数の OUT パラメータ
- 大きな型(CLOB等)は NOCOPY ヒントでパフォーマンス改善
- SQL から呼び出す → ファンクション(RETURN で1値)を使う

