CodeGym /コース /SQL SELF /ネストされたループと再帰

ネストされたループと再帰

SQL SELF
レベル 51 , レッスン 3
使用可能

ネストされたループっていうのは、他のループの中で動くループのことだよ。面接の質問コーナーを想像してみて、1つの質問からさらに細かい質問が出てくる感じ。ネストされたループのロジックはこう:外側のループがイテレーションして、そのたびに内側のループが回るんだ。

例:掛け算表

1から5までの数字の掛け算表を作る関数を書いてみよう。これはネストされたループの超定番例だね:

CREATE OR REPLACE FUNCTION generate_multiplication_table()
RETURNS VOID AS $$
BEGIN
  FOR i IN 1..5 LOOP -- 外側のループ
    FOR j IN 1..5 LOOP -- 内側のループ
      RAISE NOTICE '% x % = %', i, j, i * j; -- 結果をログに出す
    END LOOP;
  END LOOP;
END;
$$ LANGUAGE plpgsql;

-- 関数の呼び出し:
SELECT generate_multiplication_table();

動きのロジック:

  1. 外側のループがiに1から5までの値を渡す。
  2. それぞれのiについて、内側のループがjに1から5までの値を渡す。
  3. 毎回ijを組み合わせて掛け算の結果を計算する。
  4. 出力はミニ掛け算表みたいになる:
   1 x 1 = 1
   1 x 2 = 2
   ...
   5 x 5 = 25

実践:2つのテーブルの共通部分を探す

じゃあ、もうちょっと「リアル」な例を作ってみよう。例えば、2つのテーブルがあるとする:

  • students(学生、名前が入ってる)
  • courses(彼らが登録してるコース)

複数のコースに登録してる学生を探したい。ネストされたループを使ってみよう:

CREATE OR REPLACE FUNCTION find_students_with_multiple_courses()
RETURNS TABLE(student_name TEXT, course_name TEXT) AS $$
BEGIN
  FOR student IN SELECT DISTINCT student_name FROM students LOOP
    FOR course IN SELECT DISTINCT course_name FROM courses WHERE student_id = student.student_id LOOP
      RETURN QUERY SELECT student.student_name, course.course_name;
    END LOOP;
  END LOOP;
END;
$$ LANGUAGE plpgsql;

-- 関数の呼び出し:
SELECT * FROM find_students_with_multiple_courses();

再帰

再帰っていうのは、関数が自分自身を呼び出すことだよ。SQLを説明してって友達に頼んだら、「このレクチャーを読んで」って言われて、そのレクチャーがまた自分に戻ってくる感じ… 再帰と無限ループは違うから注意してね。再帰には必ず「止める条件」(もう自分を呼ばないポイント)があるんだ。

例:数値の階乗を計算する

数値nの階乗は、1からnまで全部掛けたもの。例えば5の階乗(5!)は5 * 4 * 3 * 2 * 1 = 120だよ。これを再帰で実装してみよう:

CREATE OR REPLACE FUNCTION calculate_factorial(n INTEGER)
RETURNS INTEGER AS $$
BEGIN
  -- 止める条件:0か1の階乗は1
  IF n = 0 OR n = 1 THEN
    RETURN 1;
  END IF;

  -- 関数の再帰呼び出し
  RETURN n * calculate_factorial(n - 1);
END;
$$ LANGUAGE plpgsql;

-- 関数の呼び出し:
SELECT calculate_factorial(5); -- 結果: 120

動きのロジック:

  1. nが0か1なら、1を返す。
  2. n > 1なら、関数はn - 1を引数にして自分自身を呼び出し、その値にnを掛ける。
  3. こうやって呼び出しが「積み重なって」、最後に一気に計算される。

実践例:フィボナッチ数列

フィボナッチ数列は、各数字が前の2つの数字の合計になってるやつ。最初は0, 1, 1, 2, 3, 5, 8...って続くよ。

n番目の数字を計算する関数を書いてみよう:

CREATE OR REPLACE FUNCTION fibonacci(n INTEGER)
RETURNS INTEGER AS $$
BEGIN
  -- 止める条件:最初の2つの数字は決まってる
  IF n = 0 THEN
    RETURN 0;
  ELSIF n = 1 THEN
    RETURN 1;
  END IF;

  -- 関数の再帰呼び出し
  RETURN fibonacci(n - 1) + fibonacci(n - 2);
END;
$$ LANGUAGE plpgsql;

-- 関数の呼び出し:
SELECT fibonacci(6); -- 結果: 8

いつネストされたループや再帰を使う?

  1. ネストされたループはテーブル操作に向いてる:

    • 2つのテーブル間で値を比較したいとき。
    • 複雑なデータの組み合わせを作りたいとき。
  2. 再帰はこんな時に便利:

    • 数列の計算(階乗やフィボナッチなど)。
    • 階層構造の処理(例えば商品カテゴリのツリー)。

よくあるミス

ネストされたループは、特に大きなテーブルを扱うときは「重い」処理になりがち。普通のSQLでできないときだけ使うようにしよう。

再帰を使うときは、ちゃんと「止める条件」があるか確認してね。ないと無限に関数が呼ばれて、たぶんスタックオーバーフローのエラーになるよ。

複雑なネスト構造はデバッグが大変。RAISE NOTICEで途中経過を出力して自分を助けてあげよう。

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