코딩 테스트 연습/[프로그래머스][리트코드] MySQL

[LV.4] GROUP BY > 년, 월, 성별 별 상품 구매 회원 수 구하기

duswjd_data 2025. 6. 27. 10:16

문제

https://school.programmers.co.kr/learn/courses/30/lessons/131532

 

프로그래머스

SW개발자를 위한 평가, 교육의 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr


문제 설명

  • USER_INFO 테이블과 ONLINE_SALE 테이블에서 년, 월, 성별 별로 상품을 구매한 회원수를 집계하는 SQL문을 작성
  • 결과는 년, 월, 성별을 기준으로 오름차순 정렬
  • 이때, 성별 정보가 없는 경우 결과에서 제외

정답 코드

SELECT YEAR(O.SALES_DATE) YEAR,
       MONTH(O.SALES_DATE) MONTH,
       U.GENDER,
       COUNT(DISTINCT O.USER_ID) USERS
FROM USER_INFO U JOIN ONLINE_SALE O ON U.USER_ID = O.USER_ID
WHERE U.GENDER IS NOT NULL
GROUP BY YEAR, MONTH, U.GENDER
ORDER BY YEAR, MONTH, U.GENDER

  • COUNT(DISTINCT O.USER_ID)

    → 같은 달에 여러 번 구매한 회원도 1번만 카운트
    회원 수 집계를 정확히 하기 위해 반드시 DISTINCT 필요

개선된 코드 (CTE 활용)

WITH FILTERED_SALE AS (SELECT O.USER_ID,
                              YEAR(O.SALES_DATE) AS YEAR,
                              MONTH(O.SALES_DATE) AS MONTH,
                              U.GENDER
                       FROM USER_INFO U JOIN ONLINE_SALE O ON U.USER_ID = O.USER_ID
                       WHERE U.GENDER IS NOT NULL)

SELECT YEAR, MONTH, GENDER,
       COUNT(DISTINCT USER_ID) AS USERS
FROM FILTERED_SALE
GROUP BY YEAR, MONTH, GENDER
ORDER BY YEAR, MONTH, GENDER
  • 복잡한 조인과 필터가 들어간 경우, CTE를 활용해 중간 단계를 분리하면 쿼리의 가독성과 유지보수성이 향상됨

(참고) 성능 고려 시, DATE_FORMAT 사용 가능 (MySQL)

- 단, 성능 차이가 크지 않은 상황에서는 불필요

SELECT DATE_FORMAT(O.SALES_DATE, '%Y') AS YEAR,
       DATE_FORMAT(O.SALES_DATE, '%m') AS MONTH,
       U.GENDER,
       COUNT(DISTINCT O.USER_ID) AS USERS
...

첫 번째 시도 (실패)

SELECT YEAR(O.SALES_DATE) YEAR,
       MONTH(O.SALES_DATE) MONTH,
       U.GENDER,
       COUNT(*) USERS
FROM USER_INFO U JOIN ONLINE_SALE O ON U.USER_ID = O.USER_ID
WHERE U.GENDER IS NOT NULL
GROUP BY YEAR, MONTH, U.GENDER
ORDER BY YEAR, MONTH, U.GENDER

틀린 이유

  • 문제 조건에 따라 동일한 날짜 + 회원 ID + 상품 ID 조합은 중복 없음
  • 하지만, 같은 달 안에 동일 회원이 여러 번 구매할 수는 있음
  • COUNT(*)는 구매 건수를 세기 때문에, 같은 회원도 여러 번 카운트됨

 

  • 중복 제거된 회원 수 세는 코드 생각해보기 (DISTINCT)