指定日期的产品价格:缓慢变化维的时点查询
LeetCode 1164 · 时点查询、取截止日前最后一条、默认值
面试怎么回答
这是拉链表 / 缓慢变化维的时点查询。先过滤 change_date <= '2019-08-16',按产品取日期最大的一行得到当时价格;再用全部产品 LEFT JOIN,没有调价记录的补默认值 10。数仓里查历史某天的维度属性,也是同样的思路。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
题意
价格表记录每个产品每次调价的日期和新价格。查询 2019-08-16 这一天每个产品的价格;在这天之前从未调过价的产品,价格是默认的 10。
表结构
- Products:product_id、new_price、change_date;(product_id, change_date) 为主键
思路
本质是拉链表的时点查询:对每个产品,找到不晚于目标日期的最后一次调价,它的新价格就是当天价格。先过滤 change_date ≤ 目标日,再取每个产品最大日期对应的那一行;然后用全部产品去 LEFT JOIN,没有调价记录的产品价格补成 10。
参考解法
select distinct p.product_id,ifnull(t1.new_price,10) price
from Products p
left join
(
select product_id,new_price
from (
select product_id,new_price,change_date,Max(change_date) over(partition by product_id ) md
from Products
where change_date<='2019-08-16'
)tmp
where change_date = md
)t1
on p. product_id = t1.product_id
order by price desc
另一种写法:ROW_NUMBER 取每个产品截止日前最后一次调价
SELECT p.product_id, IFNULL(t.new_price, 10) AS price
FROM (SELECT DISTINCT product_id FROM Products) p
LEFT JOIN (
SELECT product_id, new_price,
ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY change_date DESC) AS rn
FROM Products
WHERE change_date <= '2019-08-16'
) t ON p.product_id = t.product_id AND t.rn = 1
易错点
- 只在调价表里查,目标日之前没调过价的产品会丢失,必须用全部产品列表做左关联。
- 取「最后一次调价」时要先过滤日期再求最大值,顺序反了会拿到目标日之后的价格。