lead函数跨年取上一年值需按年份降序排序后使用,即lead(value, 1) over (partition by group_col order by year desc),配合聚合前置与null/零值防护,确保分组内时间连续性。

LEAD函数怎么跨年取上一年的分组汇总值
直接用 LEAD() 按年排序取下一行,无法解决“今年2024要对比2023”的需求——因为 LEAD() 默认是取「后一条」,而你需要的是「前一年」。正确做法是先按年份升序排列,再用 LEAD(value, 1) 向后看一行,这一行恰好就是**下一年的数据**;反过来,若你把年份降序排列,LEAD(value, 1) 取到的就是**上一年的数据**。
关键点在于:排序方向决定 LEAD 的语义。别硬记“LEAD 是取后”,要看你 ORDER BY 怎么排。
- 年份字段必须是数值型或可排序的日期类型(如
YEAR(order_date)),避免用字符串 '2023' 排序导致 '2024' '2024' 这种错乱 - 必须配合
PARTITION BY(如按产品类别、区域分组),否则 LEAD 会在全表范围滑动,跨组污染数据 - 如果某组缺失某一年(比如 A 类产品没有 2022 年记录),
LEAD()返回 NULL,需用COALESCE()或条件判断处理
完整写法:分组 + 年度排序 + LEAD取上年值
假设你有销售表 sales,含字段 product、amount、order_date,目标是算每个 product 每年的同比((今年 - 去年) / 去年):
SELECT
product,
yr,
amount,
prev_amount,
CASE
WHEN prev_amount IS NOT NULL AND prev_amount != 0
THEN ROUND((amount - prev_amount)::NUMERIC / prev_amount, 4)
ELSE NULL
END AS yoy_rate
FROM (
SELECT
product,
EXTRACT(YEAR FROM order_date) AS yr,
SUM(amount) AS amount,
LEAD(SUM(amount), 1) OVER (
PARTITION BY product
ORDER BY EXTRACT(YEAR FROM order_date) DESC
) AS prev_amount
FROM sales
GROUP BY product, EXTRACT(YEAR FROM order_date)
) t;
注意:ORDER BY ... DESC 是核心,它让 LEAD 向「更早的年份」取值;PARTITION BY product 确保各产品独立计算;聚合必须在窗口函数外完成(即先 GROUP BY,再套窗口),不能在窗口内直接对原始行用 LEAD。
为什么不用 LAG?什么时候 LAG 更合适
LAG() 和 LEAD() 在逻辑上完全对称:只要排序方向一致,LAG(x, 1) ORDER BY yr ASC 等价于 LEAD(x, 1) ORDER BY yr DESC。选哪个纯属风格偏好,但容易踩坑的是混用——比如写成 LAG(amount) OVER (PARTITION BY ... ORDER BY yr DESC),那就取到的是「下一年」,不是上年。
- 习惯升序排序的人,用
LAG()更直觉(“往前看一年”) - 习惯降序排序的人,用
LEAD()更顺手(“往后看一年”在降序里就是时间上更早) - PostgreSQL/Oracle/SQL Server 都支持两者;MySQL 8.0+ 也支持,但旧版不支持窗口函数,得用自连接或变量模拟
NULL 和除零问题怎么稳住结果
年度同比最常崩在两个地方:某产品第一年没上年数据(prev_amount 为 NULL),或上年销售额为 0(除零错误)。上面示例用了 CASE 检查,但实际中建议封装成可复用逻辑:
- 用
NULLIF(prev_amount, 0)替代直接除,避免报错;再配合COALESCE(..., 0)控制默认值 - 如果业务要求“首年同比显示为 0 而非 NULL”,就写
COALESCE(prev_amount, 0),但要注意:用 0 做分母仍会错,所以必须先判空再判零 - 某些场景(如同比增幅只对连续两年都存在的组计算),可在外层加
WHERE prev_amount IS NOT NULL
真正麻烦的不是语法,而是年份对齐——如果数据里只有 2022-03 和 2024-01,中间缺 2023 年,LEAD 就会跳过,直接拿 2022 当 2024 的“上年”。这种断点需要提前补全年份维度,靠窗口函数本身解决不了。










