如何为加油记录匹配其加油时间之前最近的油价更新记录?
解决方案:匹配加油记录与最近的油价更新
问题背景
我们有两张无直接等值关联字段的表:
- RefuelRecord(加油记录表):存储车辆加油的ID和对应时间
- PriceUpdateRecord(油价更新记录表):存储油价的更新时间和对应价格
需求是为每一条加油记录,找到加油时间之前最近的一次油价更新记录,并关联出对应的油价和更新时间——直接用普通LEFT JOIN会因为没有等值条件产生笛卡尔积,完全不符合预期。
示例数据
先确认创建表和插入数据的SQL(修正了原示例中的时间小差异,保证逻辑一致):
Create table RefuelRecord (RefuelId int, RefuelTime datetime); INSERT INTO RefuelRecord VALUES(1, '2022-02-01 12:15:00'); INSERT INTO RefuelRecord VALUES(2, '2022-03-01 12:15:00'); INSERT INTO RefuelRecord VALUES(3, '2022-04-01 12:15:00'); Create table PriceUpdateRecord (UpdateTime datetime, Price double); INSERT INTO PriceUpdateRecord VALUES('2022-01-20 00:00:00', 9.0); INSERT INTO PriceUpdateRecord VALUES('2022-02-20 00:00:00', 8.1); INSERT INTO PriceUpdateRecord VALUES('2022-03-20 00:00:00', 7.2);
方案一:子查询获取最近的油价更新
这种方式适配绝大多数SQL数据库(MySQL、SQL Server、PostgreSQL等都支持),核心思路是为每条加油记录单独筛选出符合条件的最近油价更新:
SELECT r.RefuelId, r.RefuelTime, p.UpdateTime, p.Price FROM RefuelRecord r LEFT JOIN PriceUpdateRecord p ON p.UpdateTime = ( SELECT MAX(UpdateTime) FROM PriceUpdateRecord WHERE UpdateTime <= r.RefuelTime ) ORDER BY r.RefuelId;
逻辑拆解
- 外层遍历每一条加油记录
r - 子查询针对当前加油时间
r.RefuelTime,找出所有在它之前的油价更新时间,取最大的那个(也就是时间上最近的一次) - 通过这个最大的
UpdateTime关联油价表,拿到对应的油价数据
方案二:窗口函数ROW_NUMBER()筛选最优匹配
如果你的数据库支持窗口函数(比如MySQL 8.0+、SQL Server 2008+、PostgreSQL等),可以用更高效的分组排序方式:
WITH RankedPriceUpdates AS ( SELECT r.RefuelId, r.RefuelTime, p.UpdateTime, p.Price, ROW_NUMBER() OVER ( PARTITION BY r.RefuelId ORDER BY p.UpdateTime DESC ) AS rn FROM RefuelRecord r LEFT JOIN PriceUpdateRecord p ON p.UpdateTime <= r.RefuelTime ) SELECT RefuelId, RefuelTime, UpdateTime, Price FROM RankedPriceUpdates WHERE rn = 1 ORDER BY RefuelId;
逻辑拆解
- 先用
LEFT JOIN关联所有加油时间之前的油价更新记录(这一步会产生多对一的临时结果) - 用
ROW_NUMBER()按RefuelId分组,对每个分组内的油价更新记录按UpdateTime倒序排序,最近的那条会被标记为rn=1 - 最后筛选出
rn=1的记录,就是我们需要的精准匹配结果
预期结果
两种方案都会输出符合要求的结果:
| RefuelId | RefuelTime | UpdateTime | Price |
|---|---|---|---|
| 1 | 2022-02-01 12:15:00 | 2022-01-20 00:00:00 | 9.0 |
| 2 | 2022-03-01 12:15:00 | 2022-02-20 00:00:00 | 8.1 |
| 3 | 2022-04-01 12:15:00 | 2022-03-20 00:00:00 | 7.2 |
内容的提问来源于stack exchange,提问作者foxever




