LAG函数需配合ORDER BY按时间排序,时间字段须唯一或加ID兜底防重复;漏ORDER BY会报错,首行返回NULL需用COALESCE处理,分组聚合时周期键要可排序且索引覆盖。

LAG 函数怎么写才能拿到上期订单金额
直接用 LAG 取上一行的订单金额,关键在 ORDER BY 必须明确按时间排序,且时间字段不能有重复值(否则排序不稳定,LAG 结果可能错位)。如果原始数据里只有日期没有时间戳,建议先用 CONVERT(DATE, order_time) 统一粒度,再用 ROW_NUMBER() OVER (PARTITION BY CONVERT(DATE, order_time) ORDER BY order_id) 处理同日多单的情况。
常见错误是漏写 OVER 子句里的 ORDER BY,SQL Server 会直接报错:Window function 'LAG' requires an OVER clause with ORDER BY。另外,LAG 默认返回 NULL(当无上一行时),别忘了用 ISNULL 或 COALESCE 处理首期数据。
示例片段:
SELECT order_date, total_amount, ISNULL(LAG(total_amount) OVER (ORDER BY order_date), 0) AS prev_amount FROM orders;
计算环比增长率时除零和 NULL 怎么处理
环比公式是 (本期 - 上期) / 上期,但上期为 0 或 NULL 时会导致除零错误或结果为 NULL。必须显式拦截这两种情况。
- 上期为 0:用
CASE WHEN prev_amount = 0 THEN NULL ELSE ... END - 上期为
NULL(即首期):和上一条合并判断,统一用prev_amount IS NULL OR prev_amount = 0 - 建议把增长率字段定义为
DECIMAL(5,4),避免浮点精度问题影响业务比对
完整计算示例:
SELECT
order_date,
total_amount,
prev_amount,
CASE
WHEN prev_amount IS NULL OR prev_amount = 0 THEN NULL
ELSE CAST((total_amount - prev_amount) AS DECIMAL(18,4)) / prev_amount
END AS mom_rate
FROM (
SELECT
CONVERT(DATE, order_time) AS order_date,
SUM(amount) AS total_amount,
LAG(SUM(amount)) OVER (ORDER BY CONVERT(DATE, order_time)) AS prev_amount
FROM orders
GROUP BY CONVERT(DATE, order_time)
) t;为什么按周/月聚合后 LAG 结果看起来“跳期”了
这不是 LAG 的问题,而是时间分组逻辑没对齐。比如用 DATEPART(WEEK, order_time) 分周,但没指定年份,2024 年第 1 周和 2023 年第 53 周会被当成同一组;或者用 YEAR(order_time) * 100 + MONTH(order_time) 拼月份,但没补零(如 20241 和 202410 会排序错乱)。
安全做法是构造可排序的周期键:
- 按月:用
YEAR(order_time) * 100 + RIGHT('0' + CAST(MONTH(order_time) AS VARCHAR(2)), 2) - 按周:用
DATEFROMPARTS(YEAR(order_time), 1, 1) + (DATEPART(WEEK, order_time) - 1) * 7得到每周起始日,再LAG
否则 LAG 会按字符串顺序取“上一行”,而不是业务意义上的“上周”。
性能差?可能是窗口函数没走索引
LAG 本身不慢,慢是因为 OVER (ORDER BY ...) 的字段没索引,导致每次执行都触发排序。尤其当订单表超千万行、又没按时间建聚集索引时,查询可能从秒级升到分钟级。
检查执行计划里是否有 Sort 算子占大头;若有,优先加索引:
- 单字段时间排序:在
order_time上建非聚集索引即可 - 带分组聚合再
LAG:索引需覆盖order_time和聚合字段(如amount),例如CREATE INDEX IX_orders_time_amount ON orders(order_time) INCLUDE (amount)
注意:如果查询中用了 CONVERT(DATE, order_time),而索引建在原始 order_time 上,SQL Server 仍能隐式利用索引——但别用 DATEADD 或函数封装时间字段,那会彻底失效。
真正容易被忽略的是时间精度和分组口径的一致性:LAG 只认你给它的排序序列,它不管这个“上期”在业务上是否合理。先确保聚合逻辑正确,再让 LAG 跟着走。

















