출처: 프로그래머스 스쿨
테이블 정보
- ONLINE_SALE: 의류 쇼핑물의 온라인 상품 판매 정보
ONLINE_SALE_ID | USER_ID | PRODUCT_ID | SALES_AMOUNT | SALES_DATE |
---|
1 | 1 | 3 | 2 | 2022-02-25 |
2 | 1 | 4 | 1 | 2022-03-01 |
4 | 2 | 4 | 2 | 2022-03-12 |
3 | 1 | 3 | 3 | 2022-03-31 |
5 | 3 | 5 | 1 | 2022-04-03 |
6 | 2 | 4 | 1 | 2022-04-06 |
2 | 1 | 4 | 2 | 2022-05-11 |
→ ONLINE_SALE_ID, USER_ID, PRODUCT_ID, SALES_AMOUNT, SALES_DATE는 각각 온라인 상품 판매 ID, 회원 ID, 상품 ID, 판매량, 판매일을 나타냄
Column name | Type | Nullable |
---|
ONLINE_SALE_ID | INTEGER | FALSE |
USER_ID | INTEGER | FALSE |
PRODUCT_ID | INTEGER | FALSE |
SALES_AMOUNT | INTEGER | FALSE |
SALES_DATE | DATE | FALSE |
- 동일한 날짜, 회원 ID, 상품 ID 조합에 대해서는 하나의 판매 데이터만 존재
문제
ONLINE_SALE 테이블에서 동일한 회원이 동일한 상품을 재구매한 데이터를 구하여, 재구매한 회원 ID와 재구매한 상품 ID를 출력하는 SQL문을 작성해주세요. 결과는 회원 ID를 기준으로 오름차순 정렬해주시고 회원 ID가 같다면 상품 ID를 기준으로 내림차순 정렬해주세요.
위의 테이블을 예시로 들자면 USER_ID 가 1인 유저가 PRODUCT_ID 가 3, 4인 상품들을 재구매하고, USER_ID 가 2인 유저가 PRODUCT_ID 가 4인 상품을 재구매 하였으므로, 결과는 다음과 같이 나와야 합니다.
풀이 과정
- 동일한 회원이 동일한 상품을 재구매한 데이터 → GROUP BY와 HAVING을 통해 구할 수 있음
- GROUP BY user_id, product_id로 회원별 특정 상품을 구매한 행 구하기
- HAVING과 COUNT를 통해 해당 행의 개수가 2개 이상이라면 재구매(구매 2번 이상)가 발생했다고 볼 수 있음
- ORDER BY로 오름차순(ASC, 생략 가능), 내림차순(DESC) 정렬
SELECT user_id, product_id
FROM online_sale
GROUP BY user_id, product_id
HAVING COUNT(1) >= 2
ORDER BY user_id, product_id DESC;
-
HAVING
- 집계 함수로 조건을 거는 기능
- 데이터를 계산한 후에 계산 결과에서 원하는 조건을 거는 기능
(예) 지난 달에 구매한 고객들 중 구매 건수가 10회 이상인 고객, 지난 달에 2회 이상 구매했는데 이번 달에는 1회만 구매한 고객 등
- GROUP BY절 뒤에 위치
- 집계 함수의 결과를 조건으로 걸기 때문에 반드시 집계함수와 GROUP BY가 선행되어야 함
- SUM과 같은 집계함수는 WHERE절에서 쓸 수 없으므로 HAVING을 사용
-
HAVING과 WHERE의 차이점
- 집계 함수 조건 vs 단일 데이터 조건
- 예를 들어 구매 건수의 합이 4회 이상인 조건을 구하고 싶을 때는 HAVING절을 써야 하고, 구매 건수가 2회 이상인 조건을 구하고 싶을 때는 WHERE절을 써야 함
- HAVING절은 집계함수로 계산을 해 놓고 조건을 거는 것이고 WHERE절은 데이터 추출 단계에서 필터링을 거는 것
추가 설명
GROUP BY user_id, product_id
- '동일한 유저'가 '같은 상품'에 대해 구매한 케이스를 구분하고, 이 경우의 수가 2가지가 넘어가면 재구매가 일어났다고 판단할 수 있음
- GROUP BY로 product_id만 묶으면 user_id의 출력되는 레코드의 수와 그룹핑 된 product_id의 레코드 수가 달라 결과가 출력될 수 없음
- GROUP BY product_id로 연산을 수행하게 되면 상품 종류에 대해서만 그룹화를 수행하므로 '동일한 유저'가 재구매한 목록을 알게 되는 게 아니라 '모든 유저'가 그 상품을 구매한 목록을 그룹짓게 됨
HAVING COUNT(1) >= 2
- count 안에 들어갈 수 있는 것
- 1, , user_id, product_id, ...
→ user_id, product_id를 grouping한 count이므로 user_id나 product_id나 개수 같음
→ NULL 데이터 없기 때문에 사용 가능
- GROUP BY절로 인해 user_id별 product_id가 하나의 그룹이 된 상태
- HAVING절은 모든 그룹에 적용됨
GROUP BY user_id, product_id
를 하면 1-3, 1-4, 2-4, 1-3, 3-5, 2-4, 1-4 총 7개의 그룹이 생성됨
- 이 그룹들 중 행을 2개 이상(=구매 2번 이상) 가진 건 1-3, 1-4, 2-4 그룹
- 행이 2개니 당연히 user_id도 2개, product_id도 2개, 기타 다른 컬럼 값의 개수도 2개
- 그렇기에
HAVING COUNT()
의 괄호 안에 *나 다른 컬럼을 넣어도 정답이 되는 것
- 다만 행을 2개 이상 갖는 그룹일지라도 하나의 행만 출력되는데, 이는 GROUP BY절이 그룹화된 결과에서 각 그룹을 대표하는 단일 행을 생성하기 때문