CodeGym /행동 /SQL SELF /중첩 루프와 재귀

중첩 루프와 재귀

SQL SELF
레벨 51, 레슨 3
사용 가능

중첩 루프는 다른 루프 안에서 동작하는 루프야. 면접에서 한 질문이 또 다른 꼬리 질문으로 이어지는 상황을 생각해봐. 중첩 루프의 논리는 이래: 바깥 루프가 반복을 돌고, 안쪽 루프가 바깥 루프의 각 반복마다 또 반복을 도는 거지.

예시: 곱셈표

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. 각 단계에서 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;

-- 함수 호출:
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. 이렇게 호출이 "쌓였다가", 한 방향으로 풀려나가.

실전 예시: 피보나치 수

피보나치 수는 각 숫자가 앞의 두 숫자를 더한 값이야. 시퀀스는 이렇게 시작해: 0, 1, 1, 2, 3, 5, 8....

n번째 피보나치 수를 구하는 함수를 짜보자:

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;

  -- 함수 재귀 호출
  RETURN fibonacci(n - 1) + fibonacci(n - 2);
END;
$$ LANGUAGE plpgsql;

-- 함수 호출:
SELECT fibonacci(6); -- 결과: 8

언제 중첩 루프와 재귀를 써야 할까?

  1. 중첩 루프는 테이블 작업에 좋아:

    • 두 테이블 간 값 비교할 때.
    • 복잡한 데이터 조합 만들 때.
  2. 재귀는 이런 데 더 좋아:

    • 시퀀스 계산(예: 팩토리얼, 피보나치 등).
    • 계층 구조 다룰 때(예: 상품 카테고리 트리).

흔한 실수들

중첩 루프는 특히 큰 테이블에서 성능이 "비싸"질 수 있어. SQL의 기본 기능으로 해결할 수 없을 때만 써.

재귀를 쓸 때는 꼭 "멈추는 조건"이 명확해야 해. 없으면 함수가 무한히 호출되고, 거의 확실하게 스택 오버플로우 에러가 날 거야.

복잡한 중첩 구조는 디버깅을 어렵게 만들 수 있어. 중간 결과를 출력하려면 RAISE NOTICE를 적극적으로 써봐.

2
과제
SQL SELF, 레벨 51, 레슨 3
잠금
중첩 반복문을 사용한 간단한 구구단
중첩 반복문을 사용한 간단한 구구단
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION