데이터 분석/SQL

[SQL] 프로그래머스 풀이 - 우유와 요거트가 담긴 장바구니 (4가지 풀이)

된장찌개냠냠 2024. 5. 26. 14:45
반응형

프로그래머스 SQL 문제 풀이: 우유와 요거트가 담긴 장바구니

문제링크: https://school.programmers.co.kr/learn/courses/30/lessons/62284

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

우유(Milk)와 요거트(Yogurt)를 동시에 구입한 장바구니가 있는지 알아보려 합니다. 우유와 요거트를 동시에 구입한 장바구니의 아이디를 조회하는 SQL 문을 작성해 주세요. 이때 결과는 장바구니의 아이디 순으로 나와야 합니다.

 

우유와 요거트가 동시에 담긴 장바구니를 조회하는 SQL문을 작성하는 것입니다. 이와 같은 쿼리는 실제로 장바구니나 교차구매 등의 고객 구매 행동 분석에 유용하게 사용될 수 있을 것으로 보이는데요. 아래 테이블은 CART_PRODUCTS 테이블의 예시 데이터입니다. 늘 그렇지만 다양한 정답이 가능해서 시도해 본 몇 가지만 소개하겠습니다.

| ID    | CART_ID | NAME                | PRICE |
|-------|---------|---------------------|-------|
| 1630  | 83      | Cereal              | 3980  |
| 1631  | 83      | Multipurpose Supply | 3900  |
| 5491  | 286     | Yogurt              | 2980  |
| 5504  | 286     | Milk                | 1880  |
| 8435  | 448     | Milk                | 1880  |
| 8437  | 448     | Yogurt              | 2980  |

 

풀이 방법 1. COUNT 활용

SELECT cart_id FROM cart_products
WHERE name in ('Milk', 'Yogurt')
GROUP BY cart_id
HAVING COUNT(DISTINCT NAME) = 2
ORDER BY CART_ID;

 

우선 WHERE 절에서 우유(Milk)나 요거트(Yogurt)가 담긴 장바구니만 필터링합니다. 이후 장바구니 ID(cart_id)로 그룹바이를 하면 우유와 요거트를 둘 다 장바구니에 담은 경우 2개의 행이 그룹핑이 될 것입니다. 단, 여기서 주의해야 할 점이 있는데요. 한 장바구니에 존재한다면 Milk와 Yogurt가 한 번만 등장할 것이라고 예상하지만(product_id라고 생각하면 보통은 한 장바구니 동일한 product_id가 2개 들어가지는 않을 것 같습니다), 이 문제에서는 2번 이상 등장할 수도 있다는 것입니다.

 

따라서 HAVING 절에서 COUNT(NAME) = 2라고만 작성하는 경우, Milk만 2번 들어간 장바구니도 조건을 만족해 버리는 경우가 생기는 것이죠. 따라서 HAVING절에서 DISTINCT NAME을 사용해서 꼭 우유와 요거트가 1번 이상 들어가는 장바구니만 가져오도록 해야 합니다.

 

아래와 같이 DISTINCT를 서브쿼리에서 먼저 사용할 수도 있는데요. 이 경우 cart_id와 name의 고유한 조합만을 가져오므로, 동일한 장바구니에 요거트가 들어간 레코드가 중복되어 있어도 1개만 가져오게 됩니다. DISTINCT 키워드 뒤에 여러 행을 사용하는 케이스는 아래 포스팅을 참고해 주세요.

2024.05.19 - [데이터 분석/SQL] - SQL 오래 사용해도 착각하기 쉬운 3가지 (COUNT(*), alias, DISTINCT)

 

SQL 오래 사용해도 착각하기 쉬운 3가지 (COUNT(*), alias, DISTINCT)

1. COUNT(*)의 의미와 오해집계함수 중 하나인 COUNT는 특정 조건에 맞는 행의 수를 계산하기 위해 사용합니다. COUNT 함수를 사용하는 가장 기본적인 방법 중 하나가 바로COUNT(*) 입니다. employees 테이

onemorepatty.tistory.com

SELECT cart_id FROM (
	SELECT DISTINCT cart_id, name
    FROM cart_products
    WHERE name IN ('Milk', 'Yogurt')) temp
GROUP BY cart_id
HAVING COUNT(*) = 2
ORDER BY cart_id

 

따라서 위와 같이 HAVING 절에서는 COUNT DISTINCT 대신 COUNT만 해도 무방한 것이죠.

 

풀이 방법 2. WHERE 절 서브쿼리 활용

SELECT DISTINCT
    cart_id
FROM cart_products
WHERE cart_id IN (
    SELECT cart_id FROM cart_products
    WHERE name = "Yogurt"
) AND cart_id IN (
    SELECT cart_id FROM cart_products
    WHERE name = "Milk"
)
ORDER BY cart_id

 

두 번째 풀이 방법은 WHERE 절에서 서브쿼리를 활용하는 방식인데요. 각각 Yogurt가 포함된 장바구니 아이디, Milk가 포함된 장바구니 아이디에 동시에 해당하는 레코드만 가져오는 것입니다. 이때 주의할 점은 처음 SELECT 절에서 DISTINCT를 사용해줘야 한다는 것인데요. 이유는 1번 풀이에서와 마찬가지로 요거트나 우유가 같은 장바구니임에도 2개 이상의 레코드로 나눠져 있을 수 있기 때문입니다. DISTINCT 말고 GROUP BY를 사용해도 됩니다.

 

풀이 방법 3. CASE WHEN 사용하기

SELECT 
    cart_id
FROM (
    SELECT *, (CASE name
        WHEN "Milk" THEN 1
        WHEN "Yogurt" THEN 2
    END) as milk_yogurt FROM cart_products
) tmp
WHERE milk_yogurt in (1, 2)
GROUP BY cart_id
HAVING SUM(DISTINCT milk_yogurt) = 3
ORDER BY cart_id

 

이번에는 CASE WHEN을 활용한 조금 특이한 풀이인데요. 우선 FROM 절에서 서브쿼리를 사용합니다. 이때 서브쿼리는 원래 테이블 cart_products에다가 milk_yogurt 컬럼을 추가한 테이블이 됩니다. 이때 milk_yogurt 컬럼에는 CASE WHEN 구문을 활용해 우유인 경우 1, 요거트인 경우 2의 값을 배정합니다.

 

이후 WHERE 절에서 milk_yogurt 컬럼이 1, 2인 행들만 필터링하고 cart_id로 그룹바이를 수행합니다. 이렇게 그룹핑되었으면 HAVING절에서 milk_yogurt의 합이 3인지 체크합니다. 이때 중요한 것은 DISTINCT를 사용해서 요거트나 우유가 여러 행 포함된 cart_id에 대해 중복을 삭제해줘야 한다는 점입니다. 우유 2개, 요거트 1개가 있는 장바구니라면 원래 milk_yogurt 컬럼은 1, 1, 2로 SUM 값이 4겠지만, DISTINCT를 넣어주면 중복된 1이 하나 사라져서 합계 값이 3이 됩니다. 즉, DISTINCT를 사용했을 때 SUM(DISTINCT milk_yogurt) 값이 3이 되려면 우유 1개 이상, 요거트 1개 이상이 담긴 장바구니여야만 하는 것이죠.

 

FROM 절에 바로 서브쿼리를 작성하지 않고 CTE로 별도로 빼서 작성하면 조금 더 깔끔한 쿼리가 될 수도 있을 것 같습니다.

 

 

풀이 방법 4. 조인 활용

WITH A AS (
    SELECT cart_id FROM cart_products
    WHERE name = "Yogurt"
),
B AS (
    SELECT cart_id FROM cart_products
    WHERE name = "Milk"
)
SELECT DISTINCT A.cart_id FROM A
INNER JOIN B USING (cart_id)
ORDER BY A.cart_id

 

CTE를 활용해 A, B를 만들어주고 두 테이블을 INNER JOIN 해줍니다. 그럼 Yogurt와 Milk가 동시에 들어있는 장바구니 ID만 남게 됩니다. 여기서 같은 상품이 한 장바구니에 여러 행으로 중복되어 기록된 경우를 처리하기 위해 DISTINCT를 활용해 줍니다. DISTINCT는 마지막 SELECT에서 추가해도 되고, CTE에서 NAME 컬럼을 추가해서 A, B를 각각 정의할 때 추가해 줘도 좋습니다.

 

 

이상으로 우유와 요거트가 담긴 장바구니 문제를 4가지 방법으로 풀어보았습니다.

 

 

프로그래머스(Programmers) 모든 문제 풀이 모음 바로가기

 

정확하고 꼼꼼하게 씁니다

마케팅, 데이터, 프로덕트, 스타트업, 일상, 유용한 정보 등에 대해 씁니다.

onemorepatty.tistory.com

모든 SQL 코딩테스트 문제 풀이 모음 바로가기

 

정확하고 꼼꼼하게 씁니다

마케팅, 데이터, 프로덕트, 스타트업, 일상, 유용한 정보 등에 대해 씁니다.

onemorepatty.tistory.com

 

반응형