본문 바로가기
Data Science/SQL

[SQL/오답] 집계 함수(MIN/MAX)를 활용한 기간 한정 데이터 추출 (LeetCode1084 Easy)

by 에르모사 쩐뉴 2026. 2. 7.

[리트코드]|1084. Sales Analysis III; WITH, MIN/MAX

https://leetcode.com/problems/sales-analysis-iii/description/

1. Problem

2019년 1분기(2019-01-01 ~ 2019-03-31) 사이에 판매된 제품을 찾아야 한다.

  • 핵심 과제: 1분기 판매 기록이 있더라도, 그 외 기간(1분기 이전 혹은 이후)에 단 한 건이라도 판매 기록이 있다면 결과에서 제외해야 한다.

2. Solution: 집계 함수(MIN/MAX)를 이용한 직관적 풀이

모든 판매 기록의 '시작점'과 '끝점'이 정해진 범위 안에 있는지 체크하는 방식이다.

3. Takeaway: 쿼리 설계의 정밀도와 성능 (객관적 분석)

  • WITH + NOT IN 방식의 고찰 (유진 님의 첫 번째 풀이):
    • 논리적으로는 완벽하지만, WITH절을 두 번 정의하고 IN과 NOT IN을 동시에 사용하면서 쿼리가 복잡해졌다.
    • NOT IN의 위험성: 유진 님이 분석하신 대로, 서브쿼리 결과에 NULL이 포함될 경우 결과 집합 전체가 공집합이 되는 SQL의 특성을 항상 경계해야 한다. (비교 대상에 NULL이 있으면 논리값이 UNKNOWN이 되기 때문)
  • HAVING MIN/MAX 방식의 우수성:
    • 효율성: 테이블을 여러 번 스캔하거나 서브쿼리를 만들 필요 없이, 단 한 번의 GROUP BY 과정에서 모든 조건을 검증할 수 있다.
    • 논리적 명확성: "가장 빠른 날짜가 1월 1일 이후이고, 가장 늦은 날짜가 3월 31일 이전이다"라는 문장은 "그 사이에만 존재한다"는 조건과 수학적으로 동일

Table: Product

+--------------+---------+
| Column Name  | Type    |
+--------------+---------+
| product_id   | int     |
| product_name | varchar |
| unit_price   | int     |
+--------------+---------+
product_id is the primary key (column with unique values) of this table.
Each row of this table indicates the name and the price of each product.
Table: Sales

+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| seller_id   | int     |
| product_id  | int     |
| buyer_id    | int     |
| sale_date   | date    |
| quantity    | int     |
| price       | int     |
+-------------+---------+
This table can have duplicate rows.
product_id is a foreign key (reference column) to the Product table.
Each row of this table contains some information about one sale.
 

Write a solution to report the products that were only sold in the first quarter of 2019. That is, between 2019-01-01 and 2019-03-31 inclusive.

Return the result table in any order.

The result format is in the following example.

 

Example 1:

Input: 
Product table:
+------------+--------------+------------+
| product_id | product_name | unit_price |
+------------+--------------+------------+
| 1          | S8           | 1000       |
| 2          | G4           | 800        |
| 3          | iPhone       | 1400       |
+------------+--------------+------------+
Sales table:
+-----------+------------+----------+------------+----------+-------+
| seller_id | product_id | buyer_id | sale_date  | quantity | price |
+-----------+------------+----------+------------+----------+-------+
| 1         | 1          | 1        | 2019-01-21 | 2        | 2000  |
| 1         | 2          | 2        | 2019-02-17 | 1        | 800   |
| 2         | 2          | 3        | 2019-06-02 | 1        | 800   |
| 3         | 3          | 4        | 2019-05-13 | 2        | 2800  |
+-----------+------------+----------+------------+----------+-------+
Output: 
+-------------+--------------+
| product_id  | product_name |
+-------------+--------------+
| 1           | S8           |
+-------------+--------------+
Explanation: 
The product with id 1 was only sold in the spring of 2019.
The product with id 2 was sold in the spring of 2019 but was also sold after the spring of 2019.
The product with id 3 was sold after spring 2019.
We return only product 1 as it is the product that was only sold in the spring of 2019.

 

1. 나의 답안

# 1분기 아닌 데이터만
WITH not_first_quarter AS (
    SELECT product_id
    FROM Sales
    WHERE sale_date < '2019-01-01' OR sale_date > '2019-03-31'
), 
# 1분기 데이터만
first_quarter AS (
    SELECT product_id
    FROM Sales
    WHERE sale_date BETWEEN '2019-01-01' AND '2019-03-31' 
)


SELECT product_id, product_name
FROM Product 
WHERE product_id IN (SELECT product_id FROM first_quarter) 
    AND product_id NOT IN (SELECT product_id FROM not_first_quarter);

Easy푸는데도 버벅댔다..

  • NULL의 습격: 만약 not_first_quarter 서브쿼리 결과에 NULL 값이 하나라도 섞여 들어오면, 전체 NOT IN 조건은 아무것도 반환하지 않는다. 다행히 여기선 product_id가 PK 성격이라 괜찮겠지만, 항상 NOT IN을 쓸 때는 NULL이 없는지 경계하기. 그래서 NOT IN 대신 NOT EXISTS를 더 선호하기도 한다. 
  • IN/NOT IN 다음에 WITH절 이름만 쓰는 것이 아니라 서브쿼리로 묶어준다. 
  • sales_date가 아닌 sale_date 철자 단복수 유심히 보기. 

 

 

2. 더 나은 답안

SELECT p.product_id, p.product_name
FROM Product p
JOIN Sales s ON p.product_id = s.product_id
GROUP BY p.product_id, p.product_name
HAVING MIN(s.sales_date) >= '2019-01-01'    #가장 빠른 날짜와, 가장 늦은 날짜가 모두 1분기에 있나?
   AND MAX(s.sales_date) <= '2019-03-31';