SQL

3-Topping Pizzas: McKinsey SQL Interview Question

sawo11 2025. 4. 28. 21:00

문제 풀이 링크: https://datalemur.com/questions/pizzas-topping-cost

 

McKinsey SQL Interview Question | DataLemur

McKinsey SQL Interview Question: Write a query to calculate the cost of 3-topping pizzas.

datalemur.com


 

🍕 3-토핑 피자 조합 비용 계산 문제 요약

목적: 서로 다른 3개 토핑으로 만들 수 있는 모든 피자 조합에 대해 총 재료 비용을 계산하고 출력

조건:

  • 토핑은 반복 없이 서로 달라야 함 (예: 'Pepperoni,Pepperoni,Onion' ❌)
  • 토핑 이름은 알파벳 순서대로 나열해야 함 (예: 'Chicken,Onions,Sausage' ✅)
  • 출력 정렬 기준:
    1. 총 비용(total_cost) 내림차순
    2. 비용이 같으면 토핑 이름 오름차순 정렬
  • 토핑 이름 사이는 띄어쓰기 없이 쉼표(,) 로 연결

1. 내 풀이

  • CROSS JOIN 으로 모든 조합 생성
    • (모든 p1 × p2 × p3 조합 → 경우의 수 엄청 많아짐)
  • 이후 WHERE 조건으로
    • p1.topping_name < p2.topping_name
    • p2.topping_name < p3.topping_name
    • 중복 조합순서 무시를 걸러냄
  • 쿼리 구조는 간단하지만, JOIN이 비효율적 (모든 조합을 만들고 거르는 방식)
  • 데이터가 많으면 느려질 수 있음
SELECT CONCAT(p1.topping_name,',', p2.topping_name, ',', p3.topping_name) AS pizza, 
  p1.ingredient_cost + p2.ingredient_cost + p3.ingredient_cost AS total_cost
FROM pizza_toppings p1
CROSS JOIN 
  pizza_toppings p2,
  pizza_toppings p3
WHERE p1.topping_name < p2.topping_name
  AND p2.topping_name < p3.topping_name
ORDER BY total_cost DESC, pizza;

 


2. 3개 조합에서 유용한 풀이

 

  • INNER JOIN 두 번을 통해 조합 생성
  • 조합 조건:
    • p1.topping_name < p2.topping_name
    • p2.topping_name < p3.topping_name
    • → 이 조건을 통해 중복 없이 알파벳 순서로 3개 토핑 조합 생성
  • 가장 빠르고 가독성 좋은 풀이
  • 단, 3개 고정 → 4개 이상 조합을 만들 때는 수정 필요

 

SELECT 
  CONCAT(p1.topping_name, ',', p2.topping_name, ',', p3.topping_name) AS pizza,
  p1.ingredient_cost + p2.ingredient_cost + p3.ingredient_cost AS total_cost
FROM pizza_toppings AS p1
INNER JOIN pizza_toppings AS p2
  ON p1.topping_name < p2.topping_name 
INNER JOIN pizza_toppings AS p3
  ON p2.topping_name < p3.topping_name 
ORDER BY total_cost DESC, pizza;

3. 조합 개수가 늘어나도 사용성이 좋은 쿼리 

 

  1. 1개 토핑부터 시작해서 점점 토핑을 추가하는 RECURSIVE CTE 사용
    • topping_numbers를 세어가면서1개 → 2개 → 3개로 조합 확장
    • 토핑 추가할 때 기존 조합(anchor)의 topping_name < 추가할 토핑(addon)의 topping_name 조건을 걸어 중복과 순서 문제 해결
    • 3개 조합이 완성된 경우만 (topping_numbers = 3) 필터링
  2. STRING_TO_ARRAY로 topping_name을 배열로 만들고, UNNEST로 토핑을 하나씩 꺼냄
    • UNNEST: 배열을 한 줄씩(row)으로 펼쳐주는 함수
    • STRING_TO_ARRAY: 문자열을 구분자(,)를 기준으로 배열로 쪼개는 함수
  3. 다시 STRING_AGG로 알파벳 순서대로 토핑 이름을 합침
    • STRING_AGG: 여러 줄(row)의 값을 하나의 문자열로 합치는 함수

 

WITH RECURSIVE all_toppings AS ( -- 1단계: 재귀적으로 조합 만들기
  -- 시작: 토핑 1개
  SELECT
    topping_name::VARCHAR,
    ingredient_cost::DECIMAL AS total_cost,
    1 AS topping_numbers
  FROM pizza_toppings

  UNION ALL

  -- 확장: 하나 더 추가
  SELECT
    CONCAT(addon.topping_name, ',', anchor.topping_name) AS topping_name,
    addon.ingredient_cost + anchor.total_cost AS total_cost,
    topping_numbers + 1
  FROM 
    pizza_toppings AS addon,
    all_toppings AS anchor
  WHERE anchor.topping_name < addon.topping_name
),

arrange AS ( -- 2단계: 토핑 정렬하기
  SELECT
    topping_name,
    UNNEST(STRING_TO_ARRAY(topping_name, ',')) AS single_topping,
    total_cost
  FROM all_toppings
  WHERE topping_numbers = 3
)

-- 3단계: 정렬된 토핑을 다시 합치고 최종 출력
SELECT
  STRING_AGG(single_topping, ',' ORDER BY single_topping) AS pizza,
  total_cost
FROM arrange
GROUP BY topping_name, total_cost
ORDER BY total_cost DESC, pizza;