드디어 프로그래머스 SQL 문제 풀이 마지막 글입니다. 그동안 생각보다 이전 내용이 생각이 안나서 답답하기도 했고, 그러면서 동시에 점점 실력이 늘고 있다는 게 느껴져서 뿌듯하기도 했던 거 같습니다. 마지막까지 잘 마무리해보겠습니다 😉
⭐⭐⭐⭐⭐
SELECT COUNT(*) AS 'COUNT',
CASE WHEN A.PARENT_ID IS NULL THEN 1
WHEN (A.PARENT_ID IS NOT NULL) AND (B.PARENT_ID IS NULL) THEN 2
WHEN (B.PARENT_ID IS NOT NULL) AND (C.PARENT_ID IS NULL) THEN 3
ELSE 4 END AS GENERATION
FROM ECOLI_DATA A LEFT JOIN ECOLI_DATA B ON A.PARENT_ID = B.ID LEFT JOIN ECOLI_DATA C ON B.PARENT_ID = C.ID
WHERE A.ID NOT IN (SELECT DISTINCT(PARENT_ID) FROM ECOLI_DATA WHERE PARENT_ID IS NOT NULL)
GROUP BY GENERATION
ORDER BY GENERATION;
저는 재귀 쿼리를 사용하지 않고 두 번의 SELF JOIN
으로 풀어서 결과는 맞으나 채점하면 틀리다고 나왔습니다. 문제 의도와 달라서 그렇다고는 생각하는데, 오히려 단순히 풀었다고 생각해요.
이 사진은 두 번의 LEFT JOIN
을 한 결과입니다. 오른쪽부터 두 개 행씩 A, B, C 테이블입니다.
PARENT_ID가 순서대로 NULL인 경우에 따라 GENERATION을 정해주었습니다.
물론 이 방식은, 세대 수가 많아지면 그 만큼의 조인을 해주어야 하기 때문에 다른 문제에 적용하기는 어려울 거 같습니다.
WITH RECURSIVE GENERATION AS(
-- PARENT_ID가 NULL인 1세대 지정
SELECT ID, 1 AS g_level
FROM ECOLI_DATA
WHERE PARENT_ID IS NULL
UNION ALL
-- Recursive: 부모 ID를 기준으로 자식 탐색
SELECT e.ID, g.g_level+1 AS g_level
FROM ECOLI_DATA e INNER JOIN GENERATION g
ON e.PARENT_ID = g.ID
)
SELECT COUNT(*) AS `COUNT`, g_level as GENERATION
FROM GENERATION g
LEFT JOIN ECOLI_DATA e
ON g.ID = e.PARENT_ID
WHERE e.ID IS NULL
GROUP BY GENERATION
ORDER BY GENERATION
그래서 재귀문을 살펴보면, WITH RECURSIVE
절에서 순서대로 GENERATION을 구하고 있음을 확인할 수 있습니다.
이런 식으로 말이죠.
과정을 뜯어서 보면,
ID g_level
1 1
2 1
2의 자식: 3, 4, 5
ID g_level
3 2
4 2
5 2
4의 자식: 6, 7
ID g_level
6 3
7 3
6의 자식: 8
ID g_level
8 4
⭐⭐
WITH YEAR_MAX AS
(SELECT YEAR(DIFFERENTIATION_DATE) AS YEAR,
MAX(SIZE_OF_COLONY) AS MAX_SIZE
FROM ECOLI_DATA
GROUP BY YEAR)
SELECT YEAR(E.DIFFERENTIATION_DATE) AS YEAR,
Y.MAX_SIZE-E.SIZE_OF_COLONY AS YEAR_DEV,
ID
FROM ECOLI_DATA E JOIN YEAR_MAX Y ON YEAR(E.DIFFERENTIATION_DATE) = Y.YEAR
ORDER BY YEAR, YEAR_DEV;
WITH
절에서 분화 연도별 가장 큰 대장균의 크기를 구합니다.
그 후, WITH
절의 테이블과 원래 테이블을 YEAR를 기준으로 JOIN
하여 쿼리를 구성합니다.
⭐⭐⭐
FISH_INFO에서
SELECT
COUNT(*) AS FISH_COUNT,
MAX(LENGTH) AS MAX_LENGTH,
FISH_TYPE
FROM FISH_INFO
GROUP BY FISH_TYPE
HAVING AVG(IF(ISNULL(LENGTH), 10, LENGTH)) >= 33
ORDER BY FISH_TYPE;
물고기 종류별 평균 길이가 33 이상인 물고기 종류 중에서 해당 열들을 조회해야하므로, FISH_TYPE으로 GROUP BY
하여 HAVING
절에서 평균이 33이상인 종류만 필터링합니다.
⭐⭐⭐⭐
DEVELOPERS 테이블에서
SELECT
DISTINCT(D.ID), D.EMAIL, D.FIRST_NAME, D.LAST_NAME
FROM DEVELOPERS D JOIN SKILLCODES C ON D.SKILL_CODE & C.CODE
WHERE C.NAME IN (SELECT NAME FROM SKILLCODES WHERE CATEGORY = 'Front End')
ORDER BY D.ID;
비트 연산자 &를 사용하여, Front End에 해당하는 스킬을 가진 개발자를 WHERE
절에서 필터링하여 구했습니다.
⭐⭐
SELECT
CONCAT(QUARTER(DIFFERENTIATION_DATE), 'Q') AS QUARTER,
COUNT(*) AS ECOLI_COUNT
FROM ECOLI_DATA
GROUP BY QUARTER
ORDER BY QUARTER;
QUARTER
함수를 사용해 분기를 구한 후 CONCAT
함수를 활용해 'Q'와 함께 출력되도록 합니다.
또한 분기별 개체 수이므로, 분기로 GROUP BY
하여 COUNT
합니다.