SQL을 공부하고 있던 와중에 프로그래머스에도 SQL 문제들이 있다는 걸 발견했다. 쭉쭉 풀다가 GROUP BY 입양 시각 구하기(2) 에서 막혔다. 느낌적으로는 0~23으로 되어있는 테이블과 ANIMAL_OUTS를 group by 한 테이블을 조인하면 될 거 같다. 풀고 나서 이해한 내용을 MySQL, Oracle로 정리했다.

MySQL

0~23 numerical cte 를 만드는 다양한 방법 중 2가지

-- MySQL의 VALUES를 이용한 방법
WITH hronly (hour) AS (
    VALUES ROW(0),ROW(1),ROW(2),ROW(3),ROW(4),ROW(5),
           ROW(6),ROW(7),ROW(8),ROW(9),ROW(10),ROW(11),
           ROW(12),ROW(13),ROW(14),ROW(15),ROW(16),ROW(17),
           ROW(18),ROW(19),ROW(20),ROW(21),ROW(22),ROW(23)
)
-- recursive cte를 활용한 방법
WITH RECURSIVE hronly AS (
    SELECT 0 AS hour
    UNION ALL
    SELECT hour+1 FROM hronly
    WHERE hour<23
)

이 문제를 처음 풀 때 cte 2개를 만들었다. 근데 cte 한 개로도 풀 수 있다는 걸 나중에 알았다.

MySQL VALUES 풀이
-- two cte using VALUES
WITH hronly (hour) AS (
    VALUES ROW(0),ROW(1),ROW(2),ROW(3),ROW(4),ROW(5),
           ROW(6),ROW(7),ROW(8),ROW(9),ROW(10),ROW(11),
           ROW(12),ROW(13),ROW(14),ROW(15),ROW(16),ROW(17),
           ROW(18),ROW(19),ROW(20),ROW(21),ROW(22),ROW(23)
),
    hrandcount AS (
    SELECT HOUR(DATETIME) AS hour, COUNT(1) AS count
    FROM ANIMAL_OUTS
    GROUP BY hour
    ORDER BY hour
)
-- NULL를 0으로 바꾸기 위해 COALESCE를 사용했다
SELECT hronly.hour, COALESCE(count,0) AS count
FROM hrandcount
RIGHT JOIN hronly ON hrandcount.hour = hronly.hour
Recursive cte 풀이
-- one recursive cte
WITH RECURSIVE hronly AS (
    SELECT 0 AS hour
    UNION ALL
    SELECT hour+1 FROM hronly
    WHERE hour<23
)
-- 0~23 hour를 조인하고 ID로 count 하기
SELECT hour, COUNT(ANIMAL_ID) AS count
FROM ANIMAL_OUTS
RIGHT JOIN hronly ON HOUR(DATETIME) = hour 
GROUP BY hour
ORDER BY hour

Oracle

0~23 numerical cte 를 만드는 다양한 방법 중 2가지

-- Oracle built-in collection
WITH hronly AS (
    SELECT column_value AS hour
    FROM TABLE(sys.ODCIVarchar2List(0,1,2,3,4,5,6,7,8,9,10,11,12,
                                    13,14,15,16,17,18,19,20,21,22,23))
)
-- using connect by level
WITH hronly AS (
    SELECT level-1 AS hour FROM dual
    CONNECT BY level < 25
)

마찬가지로 cte 2개로 풀 수도 있고 한 개로 풀 수도 있다.

Built-in collection 풀이
-- two cte using sys.ODCIVarchar2List
WITH hronly AS (
    SELECT column_value AS hour
    FROM TABLE(sys.ODCIVarchar2List(0,1,2,3,4,5,6,7,8,9,10,11,12,
                                    13,14,15,16,17,18,19,20,21,22,23))
),
    hrandcount AS (
    SELECT hour, COUNT(1) AS count
    FROM (SELECT EXTRACT(HOUR FROM CAST(DATETIME AS TIMESTAMP)) AS hour 
          FROM ANIMAL_OUTS)
    GROUP BY hour
    ORDER BY hour
)
SELECT hronly.hour, COALESCE(count,0) AS count
FROM hrandcount 
RIGHT JOIN hronly ON hronly.hour = hrandcount.hour
Connect by level 풀이
-- one cte using connect by level
WITH hronly AS (
    SELECT level-1 AS hour FROM dual
    CONNECT BY level < 25
)
SELECT hour, COUNT(ANIMAL_ID) AS count
FROM ANIMAL_OUTS
RIGHT JOIN hronly ON EXTRACT(HOUR FROM CAST(DATETIME AS TIMESTAMP)) = hour
GROUP BY hour
ORDER BY hour

Recursive cte를 이용한 방법이 제일 깔끔한 거 같다. 다음에는 recursive cte를 다양하게 사용할 수 있는 방법들을 찾아봐야겠다. 그리고 생각보다 RBDMS 마다 syntax가 많이 다른거 같다는 생각이 든다.