CodeGym /課程 /SQL SELF /巢狀迴圈與遞迴

巢狀迴圈與遞迴

SQL SELF
等級 51, 課堂 3
開放

巢狀迴圈就是一個迴圈裡面再包一個迴圈。想像你在面試被問問題,然後一個問題又引出更多細節問題。巢狀迴圈的邏輯就是:外層迴圈跑一次,內層迴圈就針對外層的每一次再跑一輪。

範例:乘法表

來寫個 function,產生 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; -- Log 結果
    END LOOP;
  END LOOP;
END;
$$ LANGUAGE plpgsql;

-- 呼叫 function:
SELECT generate_multiplication_table();

運作邏輯:

  1. 外層迴圈抓 i 從 1 到 5。
  2. 每個 i,內層迴圈抓 j 從 1 到 5。
  3. 每一步把 i 跟 j 組起來算乘法。
  4. 輸出就像一個小乘法表:
   1 x 1 = 1
   1 x 2 = 2
   ...
   5 x 5 = 25

練習:找兩個表的交集

現在來個比較「生活化」的例子。假設我們有兩個表:

  • 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;

-- 呼叫 function:
SELECT * FROM find_students_with_multiple_courses();

遞迴

遞迴就是 function 會自己呼叫自己。就像你問朋友 SQL 怎麼用,他叫你去看文件,結果文件又叫你看這堂課... 不要把遞迴跟無限迴圈搞混。遞迴一定有個「停止條件」(就是 function 不再呼叫自己的點)。

範例:計算階乘

一個數字 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;

  -- 遞迴呼叫 function
  RETURN n * calculate_factorial(n - 1);
END;
$$ LANGUAGE plpgsql;

-- 呼叫 function:
SELECT calculate_factorial(5); -- 結果:120

運作邏輯:

  1. 如果 n 是 0 或 1,就回傳 1。
  2. 如果 n > 1,function 會自己呼叫自己,參數是 n - 1,然後再乘上 n。
  3. 這樣呼叫會「堆疊」起來,最後再一起算回來。

實戰範例:Fibonacci 數列

Fibonacci 數列就是每個數字都是前兩個數字的和。開頭長這樣:0, 1, 1, 2, 3, 5, 8...。

來寫個 function 算第 n 個 Fibonacci 數:

CREATE OR REPLACE FUNCTION fibonacci(n INTEGER)
RETURNS INTEGER AS $$
BEGIN
  -- 停止條件:前兩個數已知
  IF n = 0 THEN
    RETURN 0;
  ELSIF n = 1 THEN
    RETURN 1;
  END IF;

  -- 遞迴呼叫 function
  RETURN fibonacci(n - 1) + fibonacci(n - 2);
END;
$$ LANGUAGE plpgsql;

-- 呼叫 function:
SELECT fibonacci(6); -- 結果:8

什麼時候該用巢狀迴圈跟遞迴?

  1. 巢狀迴圈很適合處理表格:

    • 比對兩個表的值。
    • 組合複雜的資料。
  2. 遞迴比較適合:

    • 算數列(像階乘、Fibonacci)。
    • 處理階層結構(像商品分類樹)。

常見錯誤

巢狀迴圈有時候效能很差,尤其是資料表很大時。真的沒辦法用一般 SQL 寫再用巢狀迴圈。

用遞迴時一定要有明確的「停止條件」。不然會無限呼叫,最後 stack overflow 掛掉。

巢狀結構太複雜會很難 debug。可以多用 RAISE NOTICE 印出中間結果幫自己。

2
任務
SQL SELF, 等級 51, 課堂 3
上鎖
用巢狀迴圈做簡單的乘法表
用巢狀迴圈做簡單的乘法表
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION