lag()计算上月销售额需按月份严格排序,推荐date类型或yyyy-mm字符串;必须配合order by窗口定义,首行返回null需显式处理除零,聚合后再开窗并确保索引优化。

用 LAG() 计算上月销售额
核心是把当月和上月销售额拉到同一行比。直接 JOIN 自关联太重,LAG() 是最轻量、可读性最强的方案。注意必须按月份严格排序,否则 LAG(sales, 1) 拿到的不是上月数据。
- 月份字段必须能正确排序:推荐用
DATE类型(如sale_date),或至少是YYYY-MM格式的字符串;2023-01比Jan-2023更安全 -
LAG()必须配合ORDER BY窗口定义,且ORDER BY要在PARTITION BY之后(如果分店/品类要分别看环比,才加PARTITION BY store_id) - 示例片段:
SELECT ym, sales, LAG(sales, 1) OVER (ORDER BY ym) AS prev_sales FROM monthly_sales;
环比增长率计算与 NULL 处理
LAG() 对首行返回 NULL,直接除会报错或结果为 NULL。不能靠 WHERE sales_ratio > 0.5 过滤——因为 NULL 不参与比较,首月自动被丢弃,但这是合理行为;真正要防的是除零(上月销售额为 0)。
- 用
COALESCE(LAG(sales, 1) OVER (...), 0)不能解决除零,反而掩盖问题;应显式判断:CASE WHEN prev_sales > 0 THEN (sales - prev_sales)::FLOAT / prev_sales END - PostgreSQL 中整数相除会截断,务必转
FLOAT或NUMERIC;MySQL 用sales * 1.0 / prev_sales;SQL Server 推荐CONVERT(DECIMAL(5,2), sales) / prev_sales - 避免写成
(sales / prev_sales) - 1 > 0.5,当prev_sales为 0 时直接崩溃
筛选增长超 50% 的月份(含边界细节)
“超 50%” 是严格大于,不是 ≥。很多业务方会忽略 0 增长和负增长月份是否该纳入初筛——这里只关心正向跃升,所以 sales > prev_sales 可提前过滤,减少计算量。
- 完整条件建议写成:
(sales - prev_sales) > 0 AND (sales - prev_sales)::FLOAT / prev_sales > 0.5 - 不要合并为单个表达式:
(sales / prev_sales) > 1.5—— 当prev_sales为 0 或负数时逻辑失效 - 如果数据含多维度(如分城市),需确认是否要全局环比,还是各城市独立计算:后者必须加
PARTITION BY city到OVER子句中,否则跨城市拉错“上月”
性能与索引注意事项
LAG() 本身不拖慢查询,慢往往出在基础扫描上。如果每月只一条记录,性能无忧;但若原始表是明细订单表,必须先聚合再开窗,否则窗口函数会在百万行上运行。
- 错误写法:
LAG(sales)直接作用于订单级表 → 每行都算一次,无意义且极慢 - 正确路径:先
GROUP BY ym汇总销售额,再对汇总结果用LAG() - 确保
ym字段有索引(或组合索引前导列),尤其当数据量大且只查最近 N 个月时,WHERE ym >= '2024-01'才能走索引
实际跑通的关键,是别让 LAG() 面对未聚合的明细数据,也别绕过上月为 0 的校验——这两处一旦出错,结果要么全空,要么报错中断。










