본문 바로가기

SQL/LeetCode

[SQL] LeetCode: 1164. Product Price at a Given Date

Product Price at a Given Date - LeetCode

 

 

주어진 컬럼은 product_id, new_price, chage_date (제품의 가격의 변화가 기록된 테이블)

이걸 가지고 2019-08-16 이라는 특정 날짜의 제품별 가격을 출력하는 문제이다.

뭔가 간단한 거 같은데 해결하는 데 꽤 오랜 시간이 걸렸다. 

 

- 가격이 변화한 경우에만 데이터가 존재

- 가격 변화 전 모든 제품의 가격은 10

 

[풀이]

-- 1. 출력: product_id, price 
-- 2. 필터: 2019-08-16의 가격 
-- 3. 정렬: X

WITH sub AS(
    SELECT product_id AS pid
         ,MAX(change_date) AS last_change_date
    FROM products
    WHERE change_date <= '2019-08-16'
    GROUP BY 1
    ) -- 기준일 전 가격 변동이 없었던 제품은 출력X

SELECT DISTINCT product_id 
      ,IF(last_change_date IS NULL, 10, new_price) AS price 
FROM products p
    LEFT JOIN sub s ON p.product_id = s.pid 
WHERE change_date = last_change_date
OR last_change_date IS NULL

 

- WITH문으로 기준일 전 가격 변동이 있었던 제품과 변동 날짜 테이블 생성 

- 조인 조건에 의해 가격 변동이 없었던 제품은 s.pid와 last_chage_date 값이 NULL

- 기준일 전에 가격 변동이 있었던 제품은 WHERE절 조건에 의해 한번만 출력됨

- 그러나 기준일 전에 변동이 없고 그 후 여러번 변동된 제품은 last_change_date IS NULL 조건에 의해 중복 출력됨.

   따라서 DISTINCT를 사용해야 함.

 

기준일을 변경해도 항상 적용할 수 있는 쿼리를 짜고 싶었다. 효율적인 쿼리인지는 잘 모르겠다..ㅜ

근데 해당 문제는 모든 제품이 무조건 언제가 한번 이상 가격 변동이 있다는 가정이 필요한 것 같다.

가격 변동이 있어야만 products 테이블이 기록될 수 있기 때문

주어진 테이블이 하나여서 가격 변동이 없었다면 해당 제품의 id를 알 수 없고 출력할 수 없으니 말이다.

 

문제 자체는 짧았는데 쉽지 않았다..

 

+) 다른 풀이

--다른 풀이 참조 

WITH cy AS (
    SELECT *
           ,RANK() OVER (PARTITION BY product_id ORDER BY change_date DESC) AS r
    FROM products 
    WHERE change_date <= '2019-08-16'
    )

SELECT product_id
      ,new_price AS price
FROM cy 
WHERE r = 1

UNION

SELECT product_id
      ,10 AS price
FROM products 
WHERE product_id NOT IN (SELECT product_id FROM cy) -- 서브쿼리를 사용해 기준일 전에 변동이 없었던 제품 필터링

 

- 윈도우 함수 RANK()를 이용해 기준일 전 가격 변동 날짜 순서 생성(최근 변동 순으로)

- r = 1로 마지막 변동 출력

- 서브쿼리를 이용해 기준일 전 변동이 없었던 제품 필터링해서 가격 10으로 출력 후 UNION으로 합치고 마무리