프로젝트 발표까지 무사히 끝났다. 프로젝트 결과는 나중에 따로 정리할 생각이다.
학습 내용
SQL에서도 Python의 for문 처럼 반복하여 결과를 출력하게 할 수 있을까?!
계속 못 풀고 있던 코딩테스트 문제가 있었다.
문제 : 프로그래머스_입양 시각 구하기(2)
오답 이유 : 0시부터 23시까지 모든 시간대에 대해 count 결과가 0이어도 행이 출력되어야 함.
하지만 단순히GROUP BY
를 한 결과로는 데이터셋에 존재하는 시간대의 결과만 도출할 수 있어 오답 처리됨.정답 탐색 과정(1) : 다른 코딩테스트에서 풀었던 문제가 유사한 형태의 결과를 요구해서, 이 때 사용한 방법(UNION 활용)을 적용할 수 있었다.
SQL -- 진짜 바보같이 풀어서 조금 부끄러움... SELECT t_h.HOUR ,IFNULL(`COUNT`,0) AS 'COUNT' FROM ( SELECT '0' AS HOUR UNION SELECT '1' AS HOUR UNION SELECT '2' AS HOUR UNION SELECT '3' AS HOUR UNION SELECT '4' AS HOUR UNION SELECT '5' AS HOUR UNION SELECT '6' AS HOUR UNION SELECT '7' AS HOUR UNION SELECT '8' AS HOUR UNION SELECT '9' AS HOUR UNION SELECT '10' AS HOUR UNION SELECT '11' AS HOUR UNION SELECT '12' AS HOUR UNION SELECT '13' AS HOUR UNION SELECT '14' AS HOUR UNION SELECT '15' AS HOUR UNION SELECT '16' AS HOUR UNION SELECT '17' AS HOUR UNION SELECT '18' AS HOUR UNION SELECT '19' AS HOUR UNION SELECT '20' AS HOUR UNION SELECT '21' AS HOUR UNION SELECT '22' AS HOUR UNION SELECT '23' AS HOUR) AS t_h LEFT JOIN ( SELECT DATE_FORMAT(DATETIME, '%k') AS HOUR ,COUNT(ANIMAL_ID) AS 'COUNT' FROM ANIMAL_OUTS GROUP BY 1 ORDER BY 1 ASC ) AS c ON t_h.HOUR = c.HOUR
정답 탐색 과정(2) : 정답은 구했는데 쿼리 행 개수도 그렇고, readable 한 쿼리도 아니고,
모든 면에서 너무 불만족스러운 쿼리를 쓰게 되어서 더 발전시키고 싶었다.Python 으로 알고리즘 문제를 풀 때 사용했던 for문과 while문 처럼,
MySQL에서도 재귀함수와 비슷한 형태로 작동하는 구문이 있을 것 같아 찾아봤다.그러다 찾게 된 재귀적 CTE_Recursive CTE
Recursive CTE
구조
- intial query : 최초의 쿼리. anchor member 라고도 불림. 이 쿼리문을 바탕으로 재귀문이 작동
- recursive query : 재귀 쿼리.
UNION ALL
이나UNION DISTINCT
를 사용해서 조인한다. - 종결 조건 : 반복문이 종료되는 시점부터 재귀 쿼리는 더 이상 행을 출력하지 않는다.
쿼리문 예시
SQL WITH RECURSIVE cte_count (n) -- 여기서 n은 컬럼 이름이다. RECURSIVE 라고 테이블명 앞에 적어야 작동한다. AS ( SELECT 1 UNION ALL -- UNION, UNION ALL, UNION DISTINCT 사용 가능 SELECT n + 1 -- sum() 같은 집계함수, GROUP BY, ORDER BY, LIMIT 그리고 DISTINCT 는 사용 불가능하다. FROM cte_count WHERE n < 3 -- 이럴 경우 n은 1에서 3까지 출력되고 종료된다. -- 3 + 1 (=4)는 조건절에 해당하지 않는 범위라서 3까지 출력 ) SELECT n FROM cte_count;
내 쿼리에 적용 : 앞선 쿼리문과 비교하면 정말 많이 발전해서 만족스럽다.
SQL WITH RECURSIVE CTE (HOUR) AS ( SELECT 0 UNION ALL SELECT HOUR + 1 FROM CTE WHERE HOUR < 23 ) SELECT CTE.HOUR ,IFNULL(`COUNT`,0) AS 'COUNT' -- LEFT JOIN으로 생기는 NULL 값 처리하기 위한 함수 FROM CTE LEFT JOIN ( SELECT DATE_FORMAT(DATETIME, '%k') AS HOUR ,COUNT(ANIMAL_ID) AS 'COUNT' FROM ANIMAL_OUTS GROUP BY 1 ORDER BY 1 ASC ) AS c ON CTE.HOUR = c.HOUR
같은 문제 다른 쿼리로 풀어보기(SQL)
- 문제 : 리트코드_급여 카테고리 별 수 구하기
- 어제 SQL 챌린지반에서 알게된 쿼리 활용법을 적용하고 싶었고, 쿼리문이 작동할 때 런타임 차이가 있을까 궁금했다.
- 물론 코딩테스트 사이트에서 푼 결과이므로... submit 할 때마다 달라지고 실제와 차이가 있을 수 있지만 그래도 혹시 모르니까!
- 어제 배웠던 것 : 함수 내부에 CASE WHEN 구문을 넣어서 특정 데이터만 선택한 결과를 출력할 수 있다.
CASE WHEN 활용
런타임은 쿼리를 이렇게 작성할 때 더 길었다.
SQL SELECT 'High Salary' AS category ,COUNT(CASE WHEN income > 50000 THEN account_id ELSE NULL END) AS accounts_count FROM Accounts UNION SELECT 'Average Salary' AS category ,COUNT(CASE WHEN income BETWEEN 20000 AND 50000 THEN account_id ELSE NULL END) AS accounts_count FROM Accounts UNION SELECT 'Low Salary' AS category ,COUNT(CASE WHEN income < 20000 THEN account_id ELSE NULL END) AS accounts_count FROM Accounts
CASW WHEN 미사용
이게 더 빨랐다. 왜 그럴까...? 하지만 쿼리는 위쪽이 더 깔끔한 것 같다.
SQL SELECT 'High Salary' AS category ,COUNT(account_id) AS accounts_count FROM Accounts WHERE income > 50000 UNION SELECT 'Average Salary' AS category ,COUNT(account_id) AS accounts_count FROM Accounts WHERE income <= 50000 AND income >= 20000 UNION SELECT 'Low Salary' AS category ,COUNT(account_id) AS accounts_count FROM Accounts WHERE income < 20000
고찰 & 기타
- SQL 쿼리를 발전시켜보려고 새로 배운 내용을 적용하고, 검색해서 알게 된 부분을 활용해보았다.
- SQL은 재밌는데 Python은 왜 정이 가지 않을까... 다음 주에는 알고리즘 풀이에 시간과 관심과 애정을 좀 더 기울여야겠다.
'내일배움캠프_데이터분석_2기 > TIL' 카테고리의 다른 글
[TIL] camp_day30_240528 (0) | 2024.05.28 |
---|---|
[TIL] camp_day29_240527 (0) | 2024.05.28 |
[TIL] camp_day27_240523 (0) | 2024.05.24 |
[TIL] camp_day26_240522 (0) | 2024.05.24 |
[TIL] camp_day25_240521 (0) | 2024.05.24 |