본문 바로가기

Algorithm

[SQL 알고리즘] 프로그래머스 - 입양 시각 구하기

✅ 문제

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

 

처음에 문제를 보고 리쿼시브 쿼리( 재귀쿼리 )를 생각하지 못했다.

 

문제 결과가 아래와 같이 0시부터 23시까지 모두 출력이되어야됐다.

 

HOUR   COUNT

0            0

1            0

2            0

3            0

 

나는 재귀쿼리하면 메뉴만들때밖에 생각하지못했는데 아직 익숙하지않은거 같다.

 

✅ 재귀 쿼리란?

  • 반복적으로 작동될 쿼리
  • 즉, 어떤 쿼리를 반복적으로 실행
  • 자료들의 계층을 파악하는데 용이

✅ 재귀 쿼리 작성 방법

  • 임시 테이블을 불러올 때 사용하던 WITH와 함께 작성
  • 테이블들을 합쳐주는 UNION을 사용
WITH RECURSIVE TEMP_NAME (Col1, Col2, …) AS(
		Non_RECURSIVE QUREY
        UNION ALL
        RECURSIVE QUREY
        )
        
SELECT COL1, COL2
FROM TEMP_NAME

 

✅ 재귀쿼리 예시 1

 

조직도를 예시로 보자면

CREATE TABLE division
(
    division_code character varying(20) NOT NULL PRIMARY KEY,
    division_name character varying(20) NOT NULL,
    upper_division_code character varying(20) NOT NULL
);

INSERT INTO division VALUES ('develop_division', '개발본부', '');
INSERT INTO division VALUES ('front_team', '프론트팀', 'develop_division');
INSERT INTO division VALUES ('infra_team', '인프라팀', 'develop_division');
INSERT INTO division VALUES ('back_team', '백엔드팀', 'develop_division');
INSERT INTO division VALUES ('web_part', '웹파트', 'front_team');
INSERT INTO division VALUES ('aos_part', 'AOS파트', 'front_team');
INSERT INTO division VALUES ('ios_part', 'IOS파트', 'front_team');
INSERT INTO division VALUES ('1_part', '1파트', 'back_team');
INSERT INTO division VALUES ('2_part', '2파트', 'back_team');

 

📚 재귀쿼리 (모든 하위 부서 찾기)

 

첫번째 케이스는 기준 부서의 조직도 아래 모든 부서를 찾아본다

WITH RECURSIVE find_division(division_code, division_name, upper_division_code, DEPTH) 
as (
  --START_QUERY
  select division_code, division_name, upper_division_code, 1
  from division
  where 1=1
  and division_code = 'develop_division'	
  
  union
  
  --REPEAT_QUERY
  select d.division_code, d.division_name, d.upper_division_code, fd.DEPTH+1
  from find_division fd
  INNER JOIN division d on d.upper_division_code = fd.division_code --case1. 조직도 아래의 모든 부서 찾기 
)
--VIEW_QUERY
select * from find_division;

 

쿼리 실행에 관하여 순서대로 설명해보면,

  1. --START_QUERY 영역에 해당 부분을 실행하여 '개발본부'의 데이터를 일단 가져온다.
  2. 기준이 되는 데이터를 find_division 이라는 임시뷰에 저장한다.
  3. --REPEAT_QUERY 영역에 해당 부분을 실행하여,
    이미 들어있는 임시뷰와 또 다시 조직도 테이블을 조인하여
    '개발본부'의 코드인 'develop_division'을 상위부서로 갖는 부서들을 찾는다.
    => 데이터상 프론트팀, 인프라팀, 백엔드팀이 나온다. 
  4. 해당 데이터도 find_division 이라는 임시뷰에 저장한다.
  5. 추가된 데이터를 대상으로 본인코드들을 상위부서로 갖는 부서들을 찾는다.
  6. 지속 반복하여 모든 하위 부서를 찾는다.

 

📚 재귀쿼리 (모든 상위 부서 찾기)

내 기준 상위 부서를 찾고 싶을수도 있다. 예를 들어, '웹파트'의 상위 부서를 찾는다고 하면, case1 해당하는 줄을 주석하고 case2에 해당하는 부분의 주석을 풀면 쉽게 '웹파트 => 프론트팀 => 개발본부' 라는 결과를 얻을 수 있다.

 

WITH RECURSIVE find_division(division_code, division_name, upper_division_code, DEPTH) 
as (
  --START_QUERY
  select division_code, division_name, upper_division_code, 1
  from division
  where 1=1
  and division_code = 'web_part'
	
  union
  --REPEAT_QUERY
  select d.division_code, d.division_name, d.upper_division_code, fd.DEPTH+1
  from find_division fd
  INNER JOIN division d on fd.upper_division_code = d.division_code
)
--VIEW_QUERY
select * from find_division;

 

📚 프로그래머스 문제

자 드디어 이제 프로그래머스 문제이다

WITH RECURSIVE A AS(
    SELECT 0 AS HOUR
    UNION ALL
    SELECT HOUR + 1 AS HOUR
    FROM A
    WHERE HOUR < 23
)
SELECT A.HOUR, COUNT(ANIMAL_ID) AS COUNT
FROM A LEFT JOIN ANIMAL_OUTS B ON A.HOUR = HOUR(B.DATETIME)
GROUP BY HOUR
ORDER BY HOUR

 

 

다음과같이 나타낼수 있다.

 

✏️공부한 내용

Recursive쿼리에 대해서 알수있었고 초기값을 위에 쓰고 union으로아래에는 반복될 쿼리를 넣고 해당값으로 계층형을 나타낼수 있다.

아직 공부가 더필요하지만 무한루프를 고려해야되고 해당 방어코드도 넣어야될수도있다.