核心思路是先按日期聚合订单金额,再用lag()计算相邻日期差值;必须显式order by date,过滤null,并根据业务决定是否补零及处理绝对值波动。

用 LAG() 计算相邻日期订单金额差值
核心思路是:把每天的订单总金额按日期排序,用 LAG() 拿到前一天的金额,再做减法。注意必须显式指定 ORDER BY date,否则窗口函数行为不可控。
常见错误是直接对原始订单表用 LAG(),结果得到的是“某笔订单 vs 上一笔订单”的差,而不是“当日汇总 vs 前一日汇总”。得先聚合再开窗:
SELECT date, daily_amount, daily_amount - LAG(daily_amount) OVER (ORDER BY date) AS diff FROM ( SELECT date, SUM(amount) AS daily_amount FROM orders GROUP BY date ) t;
为什么不能只看绝对差值?要同时考虑正负波动
订单金额可能上涨或下跌,ABS() 是必须的——否则最大下跌(比如 -8500)会被忽略,而实际业务中单日腰斩比涨 2000 更值得警觉。
另外,LAG() 对首行返回 NULL,需用 WHERE diff IS NOT NULL 过滤,否则 ORDER BY ABS(diff) DESC 会把 NULL 排在最前(不同数据库行为略有差异,但安全起见一律过滤):
-
LAG(daily_amount) OVER (ORDER BY date)第一行无前值 → 返回NULL - 差值列含
NULL会影响MAX(ABS(diff))或排序结果 - 如果想保留首日作为基准,可改用
COALESCE(LAG(...) OVER (...), daily_amount),但此时差值为 0,不参与“波动”比较
处理日期不连续时的陷阱:跳过空日还是补零?
真实数据常有缺日(比如周末无订单),这时 LAG() 拿到的是“逻辑上前一天”,而非“物理上最近一个有数据的日期”。例如 1月3日 → 1月4日 → 1月7日,LAG() 给出的差值是 1月7日减 1月4日,中间跳过了 1月5–6日。
是否需要补零取决于分析目标:
- 若关注“交易日之间的实际变化”,当前写法正确
- 若关注“自然日维度下的稳定性”,得先用
GENERATE_SERIES(PostgreSQL)或递归 CTE(MySQL 8.0+/SQL Server)补全日期,再LEFT JOIN订单汇总 - 补零后,空日的
daily_amount为 0,LAG()差值可能异常放大(如 0 → 5000 变成 +5000),需结合业务判断是否合理
性能与索引建议:GROUP BY + 窗口函数的组合优化
当订单表很大时,子查询里的 GROUP BY date 是性能瓶颈。关键点:
- 确保
date字段有索引(单独索引或联合索引前导列) - 避免在
WHERE中对date用函数,比如WHERE YEAR(date)=2024会失效索引;改用WHERE date >= '2024-01-01' AND date - 如果只要波动 Top 1,可在外层加
LIMIT 1,但注意:必须先算完所有差值再排序取 Top,不能在子查询里LIMIT,否则丢失上下文
复杂点往往不在 LAG() 本身,而在聚合粒度和日期完整性——先确认“波动”定义是按交易日还是自然日,再决定要不要补数据、怎么补。










