订阅更新

指定日期的产品价格:缓慢变化维的时点查询

SQL 实战 123 题 · 第 72 题 中等 约 2 分钟读完

LeetCode 1164 · 时点查询、取截止日前最后一条、默认值

本文目录
  1. 面试怎么回答
  2. 题意
  3. 表结构
  4. 思路
  5. 参考解法
  6. 易错点

面试怎么回答

这是拉链表 / 缓慢变化维的时点查询。先过滤 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

易错点

  • 只在调价表里查,目标日之前没调过价的产品会丢失,必须用全部产品列表做左关联。
  • 取「最后一次调价」时要先过滤日期再求最大值,顺序反了会拿到目标日之后的价格。
刷题进度保存在当前浏览器