例えば、複雑な計算やデータ処理用の関数を持っているとするよ。もしSQLクエリに関数を組み込めなかったら、こんな感じで作業することになる:
- PythonやJavaScriptみたいなプログラミング言語から関数を呼び出す。
- その結果をSQLクエリに渡す。
これ、正直めんどくさいよね!PostgreSQLなら関数をSQLクエリに直接組み込めるから、コード量も減るし、処理も速くなるし、サーバーへのリクエストも減らせる。特にこんな時に便利:
- 計算の自動化。
- データ挿入前のバリデーション。
- 既存データの修正。
SELECTで関数を呼ぶ
まずは基本から。普通のSELECTクエリで関数を使う方法を見てみよう。例えばstudentsテーブルがあって、学生の誕生日から今の年齢を返す関数を書きたいとする。
ステップ1:関数を書く
calculate_ageという関数を作って、誕生日を受け取って年齢を返すようにしよう:
CREATE OR REPLACE FUNCTION calculate_age(birth_date DATE) RETURNS INT AS $$
BEGIN
RETURN DATE_PART('year', AGE(NOW(), birth_date))::INT;
END;
$$ LANGUAGE plpgsql;
ステップ2:SELECTクエリで関数を使う
これで、テーブルの各レコードに対して関数を呼び出せる:
SELECT id, name, calculate_age(birth_date) AS age FROM students;
何が起きてる?
studentsテーブルの各行ごとにcalculate_age関数が年齢を計算する。- 返された値は
ageカラムに表示される。
結果例:
| id | name | age |
|---|---|---|
| 1 | オットー | 21 |
| 2 | アンナ | 25 |
| 3 | アレックス | 22 |
ね、全然難しくないし、結果もきれいでプロっぽいでしょ。
INSERTで関数を呼ぶ
データ挿入時にも関数は超便利。例えばlogsテーブルがあって、ユーザーのアクションを記録したいとする。関数で自動的にメッセージを作って挿入したいよね。
ステップ1:関数を作る
generate_log_messageという関数を書いて、ユーザー名とアクションを受け取ってメッセージを返すようにしよう:
CREATE OR REPLACE FUNCTION generate_log_message(username TEXT, action TEXT) RETURNS TEXT AS $$
BEGIN
RETURN username || ' performed action: ' || action || ' at ' || NOW();
END;
$$ LANGUAGE plpgsql;
ステップ2:INSERTで関数を使う
行を追加する時に関数を呼び出してメッセージを挿入しよう:
INSERT INTO logs (message)
VALUES (generate_log_message('オットー', 'サイトにログイン'));
結果:
| id | message |
|---|---|
| 1 | オットー performed action: サイトにログイン at 2023-10-26 12:00:00 |
関数が全部やってくれる:テキストのフォーマットもタイムスタンプも自動。こういうの、ルーチン作業の自動化に最高だよね。
UPDATEで関数を呼ぶ
テーブルのデータを修正する時にも関数は使える。例えばstudentsテーブルで、グループ名を新しいコース用に変換したいとする。
ステップ1:関数を作る
promote_studentという関数を書いて、古いグループ(例:101)を受け取って新しいグループ(例:201)を返すようにしよう:
CREATE OR REPLACE FUNCTION promote_student(old_group TEXT) RETURNS TEXT AS $$
BEGIN
RETURN '2' || RIGHT(old_group, LENGTH(old_group) - 1);
END;
$$ LANGUAGE plpgsql;
ステップ2:UPDATEで関数を使う
全員のグループを更新しよう:
UPDATE students
SET group_name = promote_student(group_name);
結果:
| id | name | group_name |
|---|---|---|
| 1 | オットー | 201 |
| 2 | アンナ | 202 |
| 3 | アレックス | 203 |
関数を呼ぶだけでグループ名が一気に変わるの、ちょっと魔法みたいでしょ。
WHERE条件で関数を使う
フィルタリング条件でも関数は使える。さっきの学生の年齢の例をもう少し広げてみよう。
ステップ1:年齢でフィルタ
作ったcalculate_age関数を使って、20歳より上の学生だけ選ぼう:
SELECT id, name, birth_date
FROM students
WHERE calculate_age(birth_date) > 20;
結果:
| id | name | birth_date |
|---|---|---|
| 2 | アンナ | 1998-05-15 |
| 3 | アレックス | 1999-11-09 |
ここでは、各学生の年齢を関数でその場で計算してるんだ。
集約関数との組み合わせ
ちょっと難易度アップ。22歳未満の学生の合計人数を数えたいとする。関数はCOUNT()みたいな集約関数ともバッチリ組み合わせられるよ。
SELECT COUNT(*)
FROM students
WHERE calculate_age(birth_date) < 22;
何が起きてる?
calculate_age関数がフィルタ条件で使われてる。COUNT(*)が条件に合う行数を数えてる。
実際の使い方例
データバリデーションの自動化。 例えば、全学生の年齢が普通の範囲(例:18~30歳)に入ってるかチェックしたいとする。チェック用の関数を書いてWHERE条件で使おう。
SELECT id, name
FROM students
WHERE NOT (calculate_age(birth_date) BETWEEN 18 AND 30);
データ挿入の最適化。 ネットショップで働いてるとしよう。注文の合計金額をクライアント側で計算する代わりに、テーブルordersにデータ追加時に関数で計算しちゃおう。
INSERT INTO orders (user_id, total_price)
VALUES (1, calculate_total_price(ARRAY[5, 10, 15]));
関数呼び出し時のよくあるミス
関数をガンガン使い始めると、たまにエラーに出くわすことも。よくあるパターンと対策を紹介するね:
権限が足りない。 関数やテーブルのオーナーじゃない場合、PostgreSQLが実行を拒否することがある。関数実行権限があるか確認しよう。
型が合わない。 関数に渡す引数の型に注意。例えば、関数がDATEを期待してるのに文字列を渡すとエラーになる。明示的に型変換しよう:
SELECT calculate_age('2000-01-01'::DATE);
関数内の構文ミス。 関数がエラーを返すと、クエリ全体が失敗することもある。使う前にしっかりテストしよう。
GO TO FULL VERSION