多くのプログラミング言語では、関数とプロシージャの違いはほとんどない。でもSQLでは違うんだ。PostgreSQLの関数とプロシージャは、単なるコードを実行する2つの方法じゃなくて、考え方自体が違うんだよ。
SQLの関数は、データベースのデータを変更できない。渡されたデータだけを使って結果を返すためのもの。主にSELECTクエリの中で使うために作られてる。
SQLのプロシージャは、データベースを変更するために作られてる。だからトランザクションを扱える(関数とは違って)、データベースに書き込みもできる。でもSELECTクエリの中では使えない。
ざっくり比較するとこんな感じ:
| 特徴 | 関数 (FUNCTION) | プロシージャ (PROCEDURE) |
|---|---|---|
| データを返す | ✅ はい (RETURNS ...) |
❌ いいえ(アクションだけ実行) |
| 呼び出し方法 | SELECT, PERFORM |
CALL |
| クエリ内で使える | ✅ はい | ❌ いいえ |
DOで使える |
✅ はい | ❌ いいえ |
COMMIT, ROLLBACK対応 |
❌ いいえ | ✅ はい |
| PostgreSQLに登場した時期 | 最初から | バージョン11から |
SQLでの違い
普通のSQLだと、関数は式みたいなもの:計算して値を返すんだ。 プロシージャは命令:何かをするけど、式の中では使えない。
SQLの関数
SELECT calculate_discount(200);
WHERE、ORDER BY、INSERT、UPDATEなどで使える- ピュアじゃないとダメ:データベースの状態を変えちゃダメ(IMMUTABLE/STABLEの場合)
SQLのプロシージャ
CALL process_order(123);
- 結果を返さない
COMMIT、ROLLBACK、RAISE、ループも使える
PL/pgSQLでの違い
PostgreSQLの関数は、計算のグループみたいなもの。 めっちゃ柔軟で、パラメータ渡したり、if文やループ、カーソル、サブクエリ、行やスカラー、テーブルも返せる。
PL/pgSQLの関数
CREATE FUNCTION square(x INT) RETURNS INT AS $$
BEGIN
RETURN x * x;
END;
$$ LANGUAGE plpgsql;
ポイント:
RETURNSが必須DECLARE、BEGIN、END、LOOP、IF、CASEが使えるCOMMIT/ROLLBACKは使えないSELECT、UPDATE、CHECK、WHERE、RETURNINGで呼び出せる
呼び出し例:
SELECT square(5); -- 25を返す
PL/pgSQLのプロシージャ
プロシージャはアクションを管理する仕組み。 こんな時にピッタリ:
- たくさんのステップをロジック付きで実行したいとき
- 大量のデータを更新・挿入したいとき
- トランザクション管理したいとき:
COMMIT、ROLLBACK、SAVEPOINT
CREATE PROCEDURE log_event(msg TEXT) AS $$
BEGIN
INSERT INTO logs(message) VALUES (msg);
COMMIT;
END;
$$ LANGUAGE plpgsql;
ポイント:
RETURNSなしCALLでしか呼べないCOMMIT、ROLLBACK、SAVEPOINTが使える- バッチ処理、マイグレーション、ETLに最適
呼び出し例:
CALL log_event('処理完了');
なぜ関数とプロシージャは分かれてるの?
だってSQLでの目的が違うから:
| 関数 | プロシージャ |
|---|---|
| 「何かを計算して返す」 | 「何かを実行して結果は返さない」 |
| SQLから呼び出し | コマンドとして呼び出し |
| トランザクション管理できない | トランザクション管理できる |
SELECT、JOIN、WHEREで使う |
CALLやスクリプトで使う |
プロシージャの最大のメリット — COMMIT
プロシージャは自分の中でトランザクションを管理できる。 つまり、プロシージャの中で直接:
BEGIN;
-- ロジック
SAVEPOINT point1;
-- 更新試行
ROLLBACK TO point1;
COMMIT;
ってできる。関数ではCOMMITやROLLBACKは禁止。 やろうとすると:ERROR: invalid transaction termination in functionって怒られる。
つまり、関数は決定的で安全じゃなきゃダメ、プロシージャは「汚れ仕事」もOK — クリーンアップ、ログ、挿入とか。
比較表
| 特徴 | FUNCTION |
PROCEDURE |
|---|---|---|
| 値を返す | ✅ RETURNS |
❌ |
SELECTで使える |
✅ | ❌ |
| 呼び出し | SELECT、PERFORM、DO |
CALLのみ |
| トリガーで使える | ✅ | ❌(関数のみ) |
内部トランザクション(COMMIT) |
❌ 禁止 | ✅ OK |
| OUTパラメータの使い方 | RETURNS TABLE、RECORDで |
OUTパラメータを直接 |
| 計算向き | ✅ | 🚫 向いてない |
| ETLやロード向き | 🚫 限定的 | ✅ 最適 |
| カーソル使える | ✅ はい | ✅ はい |
どっちを使う?
関数を使う場合:
- 値を返したいとき
SELECTで呼び出したり、データをフィルタしたいとき- 簡単な計算、チェック、SQLのラッパーとして
プロシージャを使う場合:
- 複雑な処理をしたいとき
- トランザクション管理が必要なとき
- バッチ処理、データ移行、アーカイブ、ログなど
GO TO FULL VERSION