订阅更新

员工近三个月累计薪水:ROWS 和 RANGE 的区别

SQL 实战 123 题 · 第 24 题 困难 约 2 分钟读完

LeetCode 579 · 滑动窗口、RANGE 窗口、排除最近一个月

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

面试怎么回答

先用 MAX(Month) OVER (PARTITION BY Id) 去掉每个人最近一个月,再按 Id 分区、Month 排序做 SUM 窗口。窗口我会写 RANGE BETWEEN 2 PRECEDING AND CURRENT ROW,按月份数值取范围;写 ROWS 的话,月份不连续时会多算更早的月份。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

题意

员工表记录每个员工每个月的薪水。对每个员工,去掉他最近的一个月,然后对剩下的每个月,计算「当月及前两个月」的薪水合计(不足三个月就按实际月份算)。结果按员工升序、月份降序。

表结构

  • Employee:Id、Month、Salary;(Id, Month) 为主键

思路

分两步:先标出每个员工的最大月份并排除;再按员工分区、月份排序做滑动窗口求和。原解法用 ROWS BETWEEN 2 PRECEDING 取前两行,但月份可能不连续(比如只有 1、3、4 月),这时「前两行」不等于「前两个月」。用 RANGE BETWEEN 2 PRECEDING AND CURRENT ROW 按月份数值取范围,才是真正的近三个月。

参考解法

select Id,Month,
sum(Salary) over(partition by Id order by Month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) Salary
from 
(
    select Id,Month,Salary,
    lead(Month,1,0) over(partition by Id order by Month) lm
    from Employee 
)t1
where lm != 0
order by  Id,Month desc

另一种写法:用 RANGE 按月份数值取窗口,月份不连续也正确

SELECT Id, Month,
       SUM(Salary) OVER (PARTITION BY Id ORDER BY Month
                         RANGE BETWEEN 2 PRECEDING AND CURRENT ROW) AS Salary
FROM (
  SELECT *, MAX(Month) OVER (PARTITION BY Id) AS max_month FROM Employee
) t
WHERE Month < max_month
ORDER BY Id, Month DESC

易错点

  • ROWS 按行数取窗口,月份有缺口时会把更早的月份算进来。
  • 排除最近一个月要在算完窗口之前还是之后?窗口只往前看,所以先排除再算结果一样;但如果窗口往后看,顺序就会影响结果。
刷题进度保存在当前浏览器