주문량이 많은 아이스크림들 조회하기
문제 링크:https://school.programmers.co.kr/learn/courses/30/lessons/133027
프로그래머스
SW개발자를 위한 평가, 교육의 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프
programmers.co.kr
유튜브 해설:https://youtu.be/h_SMMONN2bI
Q&A
질문
SQL에서 WINDOW FUNCTION은 SELECT절 말고 어디에 사용 가능해?
답변
1. 직접 사용 가능한 곳: ORDER BY 절
SELECT 절에서 구한 윈도우 함수 결과값을 화면에 출력(SELECT)하지 않더라도, 결과 정렬을 위해 ORDER BY 절에서 바로 계산하여 사용할 수 있습니다.
SELECT 직원이름, 부서, 급여
FROM 직원
ORDER BY RANK() OVER (PARTITION BY 부서 ORDER BY 급여 DESC);
2. 왜 WHERE나 GROUP BY에서는 못 쓸까요? (SQL 실행 순서)
그 이유는 데이터베이스가 SQL을 해석하고 실행하는 논리적 실행 순서 때문입니다.
실행 순서: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
윈도우 함수는 WHERE나 GROUP BY로 조건에 맞지 않는 데이터가 다 걸러지고, 그룹화까지 완료된 이후, 즉 SELECT 단계에서 살아남은 최종 결과 집합을 대상으로 계산됩니다. 그래서 그 앞 단계인 WHERE, GROUP BY, HAVING 절에서는 윈도우 함수를 인식할 수조차 없는 것이죠.
3. 실전 팁: WHERE 절에서 윈도우 함수 결과를 조건으로 쓰려면?
"각 부서별 급여 1위 직원만 뽑아라" 혹은 "최근 접속한 이력 3개만 가져와라" 같은 데이터 추출 작업을 할 때가 많습니다. 이때는 WHERE 절에 윈도우 함수를 직접 쓸 수 없으므로, 서브쿼리(Subquery)나 CTE(WITH 절)를 활용해 먼저 윈도우 함수로 순위나 값을 만들어 두고, 바깥 쿼리에서 필터링해야 합니다.
질문
WITH X AS (
SELECT (F.TOTAL_ORDER + J.TOTAL_ORDER) AS FUSION_TOTAL_ORDER , F.SHIPMENT_ID FROM FIRST_HALF AS F
JOIN JULY AS J ON F.SHIPMENT_ID = J.SHIPMENT_ID
)
WITH Y AS(
SELECT DENSE_RANK() OVER(ORDER BY FUSION_TOTAL_ORDER DESC) AS F_T, X.SHIPMENT_ID
FROM FIRST_HALF INNER JOIN X USING(SHIPMENT_ID)
)
SELECT FLAVOR
FROM FIRST_HALF INNER JOIN Y USING(SHIPMENT_ID)
WHERE F_T > 4
뭐가 문제야?
답변
1. WITH 구문의 중복 사용
가장 결정적인 원인입니다. WITH 절(CTE)을 여러 개 정의할 때는 WITH라는 키워드는 맨 처음에 딱 한 번만 쓰고, 나머지는 쉼표(,)로 이어가야 합니다. 두 번째 WITH 키워드를 쓰면 문법 에러가 발생합니다.
오류: WITH X AS (...), WITH Y AS (...)
정답: WITH X AS (...), Y AS (...)
2. USING 절과 테이블 별칭(Alias)의 충돌
Y를 정의하는 두 번째 쿼리에서 INNER JOIN X USING(SHIPMENT_ID)를 사용하셨습니다.
USING(컬럼명)을 사용하여 조인할 경우, 조인의 기준이 되는 컬럼은 두 테이블이 합쳐진 공통 컬럼으로 취급됩니다. 따라서 SELECT 절에서 X.SHIPMENT_ID처럼 특정 테이블의 별칭(Alias)을 붙이면 에러가 발생합니다. 그냥 SHIPMENT_ID라고만 적어주셔야 해요.
질문
SELECT FLAVOR,SUM(TOTAL_ORDER) FROM JULY GROUP BY FLAVOR, TOTAL_ORDER
이렇게 하면
|FLAVOR|TOTAL_OREDER|
|----------|----|
|strawberry | 520|
|strawberry | 220|
이렇게 나오는게 아니라
| FLAVOR | TOTAL_OREDER |
|---|---|
| strawberry | 740 |
이렇게 나와야 하는거 아니야? |
SELECT에 있는건 GROUP BY에 적어야 하니깐 GROUP BY FLAVOR 이런식으로 FLAVOR만 묶는건 못하잖아
답변
말씀하신 "SELECT에 있는 건 GROUP BY에 적어야 한다"는 규칙은 반은 맞고 반은 틀립니다. 정확히는 일반 컬럼에만 해당되는 규칙이고, SUM(), COUNT(), AVG() 같은 집계 함수(Aggregate Function)는 예외예요.
그 이유를 역할로 나누어 설명해 드릴게요.
GROUP BY에 적는 것: 데이터를 묶는 기준 (예: FLAVOR)
SELECT의 집계 함수: 그 묶인 그룹 안에서 계산된 결과 (예: SUM(TOTAL_ORDER))
정답
WITH X AS (
SELECT (F.TOTAL_ORDER + J.TOTAL_ORDER) AS FUSION_TOTAL_ORDER ,F.FLAVOR FROM FIRST_HALF AS F
JOIN (SELECT FLAVOR,SUM(TOTAL_ORDER) AS TOTAL_ORDER FROM JULY GROUP BY FLAVOR) AS J ON F.FLAVOR = J.FLAVOR
),
Y AS(
SELECT DENSE_RANK() OVER(ORDER BY FUSION_TOTAL_ORDER DESC) AS F_T,FLAVOR
FROM FIRST_HALF INNER JOIN X USING(FLAVOR)
)
SELECT FLAVOR
FROM FIRST_HALF INNER JOIN Y USING(FLAVOR)
WHERE F_T < 4
보다 나은 정답
WITH X AS (
SELECT F.FLAVOR, DENSE_RANK() OVER(ORDER BY F.TOTAL_ORDER +J.TOTAL_ORDER DESC) AS F_T
FROM FIRST_HALF AS F JOIN (SELECT FLAVOR, SUM(TOTAL_ORDER) AS TOTAL_ORDER FROM JULY GROUP BY FLAVOR ) AS J
ON F.FLAVOR = J.FLAVOR
)
SELECT FLAVOR
FROM FIRST_HALF INNER JOIN X USING(FLAVOR)
WHERE F_T < 4
다른 사람의 풀이
WITH LIST AS(
SELECT *
FROM FIRST_HALF
UNION ALL
SELECT *
FROM JULY
)
SELECT FLAVOR
FROM LIST
GROUP BY FLAVOR
ORDER BY SUM(TOTAL_ORDER) DESC
LIMIT 3