재귀쿼리란?

Jiyeong Kim·2025년 2월 21일

MySQL

목록 보기
1/4

프로그래머스 문제를 푸는데 뭔가 안풀려서 사람들이 푸는걸 봤더니 다들 재귀 쿼리라는 것을 사용하는거다.

프로그래머스 문제

문제 설명

ANIMAL_OUTS 테이블은 동물 보호소에서 입양 보낸 동물의 정보를 담은 테이블입니다. ANIMAL_OUTS 테이블 구조는 다음과 같으며, ANIMAL_ID, ANIMAL_TYPE, DATETIME, NAME, SEX_UPON_OUTCOME는 각각 동물의 아이디, 생물 종, 입양일, 이름, 성별 및 중성화 여부를 나타냅니다.

NAMETYPENULLABLE
ANIMAL_IDVARCHAR(N)FALSE
ANIMAL_TYPEVARCHAR(N)FALSE
DATETIMEDATETIMEFALSE
NAMEVARCHAR(N)TRUE
SEX_UPON_OUTCOMEVARCHAR(N)FALSE

문제

보호소에서는 몇 시에 입양이 가장 활발하게 일어나는지 알아보려 합니다. 0시부터 23시까지, 각 시간대별로 입양이 몇 건이나 발생했는지 조회하는 SQL문을 작성해주세요. 이때 결과는 시간대 순으로 정렬해야 합니다.

예시

SQL문을 실행하면 다음과 같이 나와야 합니다.

HOURCOUNT
00
10
20
30
40
50
60
73
81
91
102
1113
1210
1314
149
157
1610
1712
1816
192
200
210
220
230

일단 아는대로라도 풀고 싶어서 다른 방법도 찾아봤지만, 풀이의 방법은 대략적으로 2가지가 될 수 있는데,

  1. 재귀 쿼리를 사용하기.
  2. union을 사용하기.

가 있었다.

union으로 푸는 방법은 다음과 같은데

select HOUR, max(COUNT) COUNT from
    (
        (select 0 HOUR, 0 COUNT) UNION // 모든 시간대를 생성. (0~23시, 입양간 동물 카운트)
        (select 1, 0) UNION 
        (select 2, 0) UNION 
        (select 3, 0) UNION 
        (select 4, 0) UNION 
        (select 5, 0) UNION 
        (select 6, 0) UNION 
        (select 7, 0) UNION 
        (select 8, 0) UNION 
        (select 9, 0) UNION 
        (select 10, 0) UNION 
        (select 11, 0) UNION 
        (select 12, 0) UNION 
        (select 13, 0) UNION 
        (select 14, 0) UNION 
        (select 15, 0) UNION 
        (select 16, 0) UNION 
        (select 17, 0) UNION 
        (select 18, 0) UNION 
        (select 19, 0) UNION 
        (select 20, 0) UNION 
        (select 21, 0) UNION 
        (select 22, 0) UNION 
        (select 23, 0) UNION 
        (select hour(datetime) HOUR, count(*) COUNT from animal_outs group by 1)
    ) TMP
group by 1 
order by 1 asc

서브쿼리로 각 시간마다 입양간 동물을 카운트하고 union으로 다 직접 합쳐서 나열하는 방식이다.
가능은 하지만 비효율적인 방식이기 때문에 대부분의 사람들이 재귀 쿼리를 사용한 것으로 보인다.

그래서 재귀쿼리가 뭔데요


뜻을 보자면 재귀의 영문명인 recursive는 '반복되는', '순환하는'의 뜻을 가진다.
쉽게 말해서 자기 자신을 응용할 수 있는 쿼리를 말한다.

유사한 말로, 재귀, recursion은 자기 자신을 서브루틴으로 부르는 형태를 말한다.

for문 처럼 일종의 반복문처럼 활용할 수 있다.

1. 재귀 쿼리의 형태

WITH RECURSIVE cte_name (column_name(s)) // 1. WITH RECURSIVE 의 형태로 재귀쿼리를 사용할 것이라 선언
AS (									 // 2. AS(재귀 쿼리문 작성)
    -- Base case						 // 3. 재귀 쿼리문: Base case + UNION(ALL) + Recursive case
    SELECT ...						     //    		     기본 케이스(루프의 시작)와 재귀 케이스를 
    UNION ALL							 //				 유니온 또는 유니온 올로 결합하여 작성
    -- Recursive case					 // 4. recursive case의 FROM 절이 윗쪽의 cte_name을 받으면서 재귀(자체참조)
    SELECT ... 							 // 5. 이때 Where 절이 terminate 조건이 됨
    FROM cte_name ...					
)
SELECT * FROM cte_name; // 결과 테이블CTE을 사용할 쿼리
  • 참고로 CTE(Common Table Expression)는 임시로 이름이 지정된 결과 집합을 의미하고, 결과테이블이라고도 한다.
    반복되어 생성되는 이 테이블은 쿼리가 끝날때까지만 임시로 메모리에 저장되었다가 삭제된다.

참조
https://inpa.tistory.com/entry/MYSQL-📚-RECURSIVE-재귀-쿼리#
https://codedamn.com/news/sql/recursive-queries-in-sql

이래도 이해가 안된다면 이 영상을 다시 보고 이해하기

2. 정답 코드 분석

WITH RECURSIVE TBL  // 0시~23시까지의 시간을 뽑아야 하므
AS (
    SELECT			// 기본 쿼리를 0, HOUR로 지정
        0 AS HOUR    

    UNION ALL    

    SELECT HOUR + 1  // 재귀쿼리는 +1씩 더해지고
    FROM TBL
    WHERE HOUR < 23 // 23 미만일때 종료됨 (22+1=23이니까!)
    )

SELECT 
    t.HOUR,
    COUNT(a.ANIMAL_ID) AS COUNT
FROM
    TBL t 			// tbl과, 
    LEFT JOIN (SELECT 	// 각 시간마다 입양된 동물을 뽑는 쿼리를 레프트 조인함(빈값 고려)
                ANIMAL_ID,
                HOUR(DATETIME) AS HOUR
               FROM
                ANIMAL_OUTS) a
    ON t.HOUR = a.HOUR // 시간을 기준으로 조인
GROUP BY
    t.HOUR
ORDER BY
    t.HOUR ASC;
profile
해봅시다

0개의 댓글