CodeGym /コース /SQL SELF /関数とプロシージャの相互作用

関数とプロシージャの相互作用

SQL SELF
レベル 55 , レッスン 1
使用可能

ちょっと前のレベルで、PostgreSQLのプロシージャと関数について話したよね。今回はもっと深く掘り下げていこう。

関数とプロシージャは独立して動かすこともできるけど、たいていはその連携がシステム全体の成功を左右するんだ。一番便利なのは、関数を他の関数から呼び出してデータを渡したり、実行結果を受け取ったりできることだよ。

関数 vs プロシージャ:何が違うの?

PostgreSQLで関数とプロシージャがどう違うか、思い出してみよう:

  • 関数(FUNCTION

    • 値を返す。
    • SELECTで使える。
    • 計算やデータ変換によく使われる。
  • プロシージャ(PROCEDURE

    • 直接値を返さない。
    • データの挿入、更新、削除みたいな操作に使う。
    • CALLコマンドで呼び出す。

関数間のデータの受け渡し

実践に進もう。まずは関数とプロシージャ間でデータを渡す基本的な例から。基本的に、関数間のデータ受け渡しはパラメータや返り値を使ってやるよ。

関数の中で別の関数を呼ぶのはこんな感じ:

CREATE OR REPLACE FUNCTION get_student_name(student_id INT)
RETURNS TEXT AS $$
DECLARE
    student_name TEXT;
BEGIN
    -- 学生IDで名前を取得する
    SELECT name INTO student_name FROM students WHERE id = student_id;

    -- 名前を返す
    RETURN student_name;
END;
$$ LANGUAGE plpgsql;

この関数は他の関数から呼び出せる:

CREATE OR REPLACE FUNCTION welcome_student(student_id INT)
RETURNS TEXT AS $$
DECLARE
    message TEXT;
BEGIN
    -- 他の関数で学生の名前を取得
    message := 'ようこそ, ' || get_student_name(student_id) || '!';

    -- 挨拶メッセージを返す
    RETURN message;
END;
$$ LANGUAGE plpgsql;
  1. get_student_name関数は、学生のID(student_id)から名前を返す。
  2. もう一つの関数welcome_studentでは、その名前を使って挨拶メッセージを作ってる。

メモ: SELECT INTOでデータを取り出すと、PL/pgSQLの変数にクエリ結果が入るよ。

関数からプロシージャを呼ぶ例

今度は、関数からプロシージャを呼び出す方法を見てみよう。例えば、学生がシステムにログインした時間を記録するプロシージャがあるとする:

CREATE OR REPLACE PROCEDURE log_student_entry(student_id INT)
LANGUAGE plpgsql
AS $$
BEGIN
    INSERT INTO log_entries(student_id, entry_time)
    VALUES (student_id, NOW());
END;
$$;

このプロシージャを関数から呼び出して、ログインを記録しつつメッセージを返す:

CREATE OR REPLACE FUNCTION student_login(student_id INT)
RETURNS TEXT AS $$
BEGIN
    -- ログ記録用プロシージャを呼ぶ
    CALL log_student_entry(student_id);

    -- メッセージを返す
    RETURN '学生のログインが正常に記録されたよ。';
END;
$$ LANGUAGE plpgsql;

実践的な相互作用の例

例1:注文の合計金額計算とログ記録

オンライン注文システムを扱っているとしよう。注文の合計金額を計算する関数がある:

CREATE OR REPLACE FUNCTION calculate_order_total(order_id INT)
RETURNS NUMERIC AS $$
DECLARE
    total NUMERIC;
BEGIN
    -- 注文の全アイテムを合計
    SELECT SUM(price * quantity) INTO total
    FROM order_items
    WHERE order_id = order_id;

    RETURN total;
END;
$$ LANGUAGE plpgsql;

注文の合計金額を保存するプロシージャ:

CREATE OR REPLACE PROCEDURE log_order_total(order_id INT, total NUMERIC)
LANGUAGE plpgsql
AS $$
BEGIN
    INSERT INTO order_totals(order_id, total)
    VALUES (order_id, total);
END;
$$;

これらを組み合わせてみよう:

CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS TEXT AS $$
DECLARE
    total NUMERIC;
BEGIN
    -- 合計金額を計算する関数を呼ぶ
    total := calculate_order_total(order_id);

    -- プロシージャで合計金額を記録
    CALL log_order_total(order_id, total);

    RETURN '注文が正常に処理されたよ。';
END;
$$ LANGUAGE plpgsql;

例2:学生の最高評価を取得してプロフィールを更新

最高評価を取得する関数:

CREATE OR REPLACE FUNCTION get_highest_rating(student_id INT)
RETURNS INT AS $$
DECLARE
    max_rating INT;
BEGIN
    -- 学生の最高評価を探す
    SELECT MAX(rating) INTO max_rating
    FROM ratings
    WHERE student_id = student_id;

    RETURN max_rating;
END;
$$ LANGUAGE plpgsql;

学生プロフィールを更新するプロシージャ:

CREATE OR REPLACE PROCEDURE update_student_profile(student_id INT, max_rating INT)
LANGUAGE plpgsql
AS $$
BEGIN
    UPDATE students
    SET highest_rating = max_rating
    WHERE id = student_id;
END;
$$;

これらを呼び出す関数:

CREATE OR REPLACE FUNCTION refresh_student_profile(student_id INT)
RETURNS TEXT AS $$
DECLARE
    max_rating INT;
BEGIN
    -- 最高評価を取得
    max_rating := get_highest_rating(student_id);

    -- 学生プロフィールを更新
    CALL update_student_profile(student_id, max_rating);

    RETURN 'プロフィールが正常に更新されたよ。';
END;
$$ LANGUAGE plpgsql;

相互作用でよくあるミス

一番ありがちなミスは、関数とプロシージャ間でデータ型が合ってないこと。例えば、プロシージャがNUMERIC型を期待してるのにINTEGERを渡すと、PostgreSQLは型不一致のエラーを出す。データ型がちゃんと合ってるか、いつも確認しよう。

もう一つのミスは、関数Aが関数Bを呼び、BがまたAを呼ぶみたいな循環呼び出し。これやると無限ループになってシステムが落ちるよ。

実用的な意味

なんでこんな相互作用が必要なの? 実際には、関数とプロシージャは複雑なシステムの「ブロック」みたいなもの。コードを独立したパーツに分けられるから、デバッグや再利用、テストがめっちゃ楽になる。例えば:

  • 面接で「複雑な操作を実行するためにプロシージャを呼ぶ関数を書いて」と言われることがある。こういう実践的なスキルを見せられるとポイント高いよ。
  • ネットショップやログシステム、CRMみたいな本番アプリを作るとき、関数とプロシージャの連携をうまく設計できるとコードがすごくシンプルになる。

関数とプロシージャの連携をもっと深く知りたいなら、PL/pgSQLの公式ドキュメントも見てみてね。

コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION