Database

[MySQL] WITH, WITH RECURSIVE 사용법

dalgorithm 2022. 10. 10. 19:49
728x90

 


CTE (Common Table Expression) 이란?

 

CTE란 기존의 뷰나 파생 테이블, 임시 테이블 등으로 사용되는 것들을 대신할 수 있고, 보다 더 간결한 표현을 사용할 수 있따는 장점이 있다. CTE는 Non-Recursive(비재귀) CTE 와 Recursive(재귀) CTE 로 두가지 종류가 있다.

 

WITH 구문은 메모리 상에 가상의 테이블을 저장할 때 사용된다.

RECURSIVE의 여부에 따라 재귀, 비재귀 두 가지 방법으로 사용 가능하다.

 

WITH CTE_테이블명(열이름1, 열이름2, 열이름3...)
AS
(
	<쿼리문>
)
SELECT 열이름1, 열이름2, 열이름3 ... FROM CTE_테이블명
--------------------------------------------------------------
WITH [RECURSIVE] TABLE명 AS (
    SELECT - # 비반복문. 무조건 필수
    [UNION ALL] # RECURSIVE 사용 시 필수. 다음에 이어붙어야 할 때 사용
    SELECT - 
    [WHERE -] # RECURSIVE 사용 시 필수. 정지 조건 필요할 때 사용
)

 


❓프로그래머스 59413번

프로그래머스 SQL. 입양 시각 구하기2 

(https://school.programmers.co.kr/learn/courses/30/lessons/59413)

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

ANIMAL_OUTS 테이블로부터 

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

에 대한 결과를 출력해야한다.

 

 시도

처음에는 ANIMAL_OUTS에 있는 DATETIME 값에 대해 깊게 생각해보지 않은 채로 바로 아래와 같이 시도하였다.

SELECT HOUR(DATETIME) AS HOUR, COUNT(DATETIME) AS COUNT
FROM ANIMAL_OUTS
WHERE  HOUR(DATETIME) >= 0 and HOUR(DATETIME) < 23
GROUP BY HOUR(DATETIME)
ORDER BY HOUR(DATETIME) ASC;

 

실행결과는 0부터 23시까지에 대한 결과가 아닌, ANIMAL_OUTS에 존재하는 시간 값에 대해서만 나와 9시에 대한 정보부터 나오는 문제가 있었다. 풀다가 도저히 감이 오지 않아 해결법을 찾아 보던 중 WITH RECURSIVE문을 사용하는 방법을 알게 되었다. 

 

위에서 언급한 Recursive CTE를 통해 WITH  RECURSIVE구문을 사용한다. WITH RECURSIVE를 통해 가상의 테이블을 생성하고 문제에서 주어지는 ANIMAL_OUTS와 JOIN 연산을 통해 해결하는 방법이다. 

 

우선 'SELECT ROW_NUMBER() OVER(ORDER BY DATETIME)-1 AS HOUR'을 통해 초기값으로 하는 가상의 테이블 Y를 생성한다. 

 

특정 테이블의 행번호를 붙여서 데이터를 조회를 해야할 때, ROW_NUMBER()을 사용하면 된다.

아래와 같은 경우, ANIMAL_OUTS 테이블에서  DATETIME을 기준으로 행번호를 붙여 정렬하는 쿼리다.

문제에서 0시부터 23시까지를 요구하고 있으므로, LIMIT 24와 HOUR에 대해 -1 처리를 해주었다.

 

WITH RECURSIVE Y AS (
SELECT ROW_NUMBER() OVER(ORDER BY DATETIME)-1 AS HOUR
FROM ANIMAL_OUTS 
LIMIT 24 )

 

그 다음으로는 가상의 테이블 Y와 ANIMAL_OUTS에 대해 LEFT JOIN을 해준다. JOIN문은 생각보다 간단하지만, 처음 접하는 WITH RECURSIVE 개념으로 해결하는데 많은 자료를 찾아보게 되었던 문제이다.

 

SELECT HOUR , COUNT(ANIMAL_ID) AS COUNT
FROM Y
LEFT JOIN ANIMAL_OUTS
ON DATE_FORMAT(DATETIME,"%H") = Y.HOUR
GROUP BY HOUR
ORDER BY HOUR

 

아래는 성공적으로 제출된 최종 답안이다.

 

✅ 최종 답안

WITH RECURSIVE Y AS (
SELECT ROW_NUMBER() OVER(ORDER BY DATETIME)-1 AS HOUR
FROM ANIMAL_OUTS 
LIMIT 24 )

SELECT HOUR , COUNT(ANIMAL_ID) AS COUNT
FROM Y
LEFT JOIN ANIMAL_OUTS
ON DATE_FORMAT(DATETIME,"%H") = Y.HOUR
GROUP BY HOUR
ORDER BY HOUR
728x90