class="layout-aside-right paging-number">
본문 바로가기
TIL

[TIL173] leetcode - Find Stores with Inventory Imbalance

by heestory323 2026. 6. 25.

문제 설명

매장별 재고 정보를 이용하여 재고 불균형이 발생한 매장을 찾는 문제였다.

문제에서 먼저 각 매장의

  • 가장 비싼 제품
  • 가장 저렴한 제품

을 찾고,

두 제품의 재고량을 비교하여

가장 비싼 제품 재고 < 가장 저렴한 제품 재고

인 경우만 출력해야 했다.

또한 각 매장은 최소 3개 이상의 상품을 판매해야 하며,

불균형 비율은

cheapest_quantity / most_expensive_quantity

로 계산하여 출력해야 했다.

출력 컬럼은 다음과 같다.

  • store_id
  • store_name
  • location
  • most_exp_product
  • cheapest_product
  • imbalance_ratio

이 문제는 그룹별 최대/최소값을 찾은 뒤, 다시 원본 데이터와 연결하여 최대·최소 행(Row)을 추출하는 Self Join 문제였다.


내가 처음 생각한 접근

처음에는 문제를 다음과 같은 순서로 생각했다.

매장별 최고가 찾기

↓

매장별 최저가 찾기

↓

원본 테이블과 다시 JOIN

↓

비싼 상품과 싼 상품 비교

이 접근 자체는 맞았다.

그래서 먼저

MAX(price)
MIN(price)

를 이용하여 매장별 최고가와 최저가를 구한 뒤,

다시 Inventory 테이블과 JOIN하여 최고가 상품과 최저가 상품만 남기는 방식으로 접근했다.

이 부분은 정답과 거의 동일한 흐름이었다.


내가 헷갈렸던 부분

1. Self Join에서는 별칭의 역할을 끝까지 유지해야 한다.

이번 문제에서는

f1 = 가장 비싼 상품

f2 = 가장 저렴한 상품

으로 역할을 정했다.

그러면

JOIN 조건

출력 컬럼

비율 계산

모든 곳에서 이 역할을 그대로 유지해야 한다.

예를 들어

f1.price > f2.price

라면

항상

f1 = 비싼 상품

f2 = 싼 상품

이다.

처음에는 중간에 역할이 헷갈려서

출력 컬럼과 계산식이 서로 뒤바뀌기도 했다.

이번 문제를 통해 Self Join에서는 별칭(f1, f2)의 역할을 처음 정했다면 끝까지 바꾸지 않는 것이 가장 중요하다는 것을 배웠다.


2. 그룹별 최대값을 찾는 것과 최대값인 행(Row)을 찾는 것은 다르다.

처음에는

MAX(price)

만 구하면 끝이라고 생각했다.

하지만

MAX(price)

가격만 알려줄 뿐,

어떤 상품인지 알려주지는 않는다.

예를 들어

상품가격

A 100
B 50
C 10

이라면

MAX(price)

100

만 반환한다.

하지만 문제는

가장 비싼 상품 이름

가장 비싼 상품 수량

도 필요했다.

그래서

최대값 계산

↓

원본 테이블과 다시 JOIN

↓

최대값인 행 추출

과정이 반드시 필요했다.

이 부분이 이 문제의 핵심 패턴이었다.


질문하며 이해한 내용

Q1.

왜 MAX(price)만으로는 해결되지 않을까?

→ MAX(price)는 가격만 반환한다.

→ 상품명과 수량을 함께 가져오기 위해서는 원본 테이블과 다시 JOIN해야 한다.

집계값과 원본 행(Row)은 서로 다르다.


Q2.

왜 Self Join을 해야 할까?

→ 최고가 상품과 최저가 상품을 같은 행에서 비교해야 하기 때문이다.

→ 각각 한 행으로 만든 뒤 같은 매장끼리 JOIN하면 쉽게 비교할 수 있다.


Q3.

비율 계산이 왜 계속 헷갈렸을까?

→ 문제에서 요구한 공식이 아니라 내가 자연스럽게 생각한 공식을 사용했기 때문이다.

비율 문제는 반드시 문제에서 제시한 계산식을 그대로 사용해야 한다.


최종 해결 방법

1단계. 매장별 최고가와 최저가를 구한다.

먼저

매장

↓

최고가

최저가

를 계산한다.


2단계. 최고가와 최저가인 상품만 다시 추출한다.

가격만으로는 상품명을 알 수 없기 때문에

Inventory와 다시 JOIN한다.

이제

비싼 상품

↓

싼 상품

두 행만 남는다.


3단계. 같은 매장의 최고가 상품과 최저가 상품을 연결한다.

Self Join을 이용하여

한 행에서

  • 최고가 상품
  • 최저가 상품

을 동시에 비교할 수 있도록 만든다.


4단계. 문제 조건을 적용한다.

재고 불균형 조건

비싼 상품 재고

<

싼 상품 재고

를 만족하는 경우만 남긴다.


5단계. 불균형 비율을 계산한다.

문제에서 제시한 공식 그대로

cheapest_quantity

/

most_expensive_quantity

를 계산한다.


내가 작성한 쿼리

WITH a AS (
    SELECT
        i.store_id,
        store_name,
        location,
        MAX(price) AS max_price,
        MIN(price) AS min_price
    FROM inventory i
    JOIN stores s
        ON i.store_id = s.store_id
    GROUP BY
        i.store_id,
        store_name,
        location
    HAVING COUNT(DISTINCT product_name) > 2
),
f AS (
    SELECT
        i.store_id,
        a.store_name,
        a.location,
        product_name,
        quantity,
        price
    FROM a
    JOIN inventory i
        ON a.store_id = i.store_id
       AND (i.price = max_price OR i.price = min_price)
)

SELECT
    f1.store_id,
    f1.store_name,
    f1.location,
    f1.product_name AS most_exp_product,
    f2.product_name AS cheapest_product,
    ROUND(f2.quantity / f1.quantity, 2) AS imbalance_ratio
FROM f f1
JOIN f f2
    ON f1.store_id = f2.store_id
   AND f1.price > f2.price
   AND f1.quantity < f2.quantity
ORDER BY
    imbalance_ratio DESC,
    f1.store_name;

핵심 SQL 개념 정리

MAX()/MIN() 이후 원본 테이블 재조인

왜 사용하는가?

최대값과 최소값 자체가 아니라

그 값을 가진 행(Row) 을 가져오기 위해 사용한다.

실무 활용

  • 최고 매출 상품 찾기
  • 최저 재고 상품 찾기
  • 최고 연봉 직원 찾기

면접 포인트

집계 함수는 값만 반환한다.

행(Row)이 필요하면 원본 테이블과 다시 JOIN해야 한다.


Self Join

왜 사용하는가?

같은 매장 안에서

최고가 상품과 최저가 상품을 한 행에서 비교하기 위해 사용한다.

실무 활용

  • 최고값 vs 최저값 비교
  • 이전 상태 vs 현재 상태 비교
  • 부모-자식 관계 분석

면접 포인트

Self Join에서는 별칭의 역할을 처음부터 끝까지 일관되게 유지해야 한다.


함수 비교 정리

CASE WHEN vs Self Join

이번 문제에서는 CASE WHEN으로 해결할 수 있는 문제가 아니라

최고가 상품과 최저가 상품이라는 서로 다른 두 행을 비교해야 했다.

따라서 Self Join이 더 적절한 방법이었다.


이번 문제에서 배운 패턴

그룹별 최대·최소값 계산

↓

원본 테이블과 재조인

↓

최대·최소 행 추출

↓

Self Join

↓

조건 비교

↓

비율 계산

비슷한 문제가 나오면 어떻게 접근할까?

다음과 같은 유형에서 같은 패턴을 사용할 수 있다.

  • 최고가 상품 vs 최저가 상품 비교
  • 최고 연봉 직원 vs 최저 연봉 직원 비교
  • 최고 매출 상품 vs 최저 매출 상품 비교
  • 최대값과 최소값을 가진 행(Row) 비교 문제

공통적으로

"집계값을 먼저 구한 뒤, 원본 테이블과 다시 연결하여 행(Row)을 가져오는 패턴" 을 먼저 떠올린다.


오늘 배운 점

최대값과 최소값을 구하는 것과 최대값·최소값을 가진 행(Row)을 가져오는 것은 완전히 다른 문제라는 것을 배웠다.

또한 Self Join에서는 별칭의 역할을 처음부터 끝까지 일관되게 유지해야 실수를 줄일 수 있다.


다음에 같은 문제를 만나면

먼저

최대값인가?

↓

최대값을 가진 행인가?

를 구분한 뒤,

필요하다면 집계 → 재조인 → Self Join 순서로 접근한다.


핵심 한 줄 요약

그룹별 최대·최소값 비교 문제는 집계 → 원본 테이블 재조인 → Self Join 패턴으로 해결하며, 비율 계산식은 반드시 문제에서 제시한 공식을 그대로 적용해야 한다.