LAG和LEAD是识别转折点的第一选择,因其可直接获取相邻行业务值,无需自连接或子查询,避免笛卡尔积;使用时必须显式指定ORDER BY,否则结果随机;真实转折需结合方向性条件判断,并显式处理NULL边界。

为什么 LAG 和 LEAD 是识别转折点的第一选择
因为它们能直接拿到“上一行”或“下一行”的业务值,无需自连接或子查询,避免笛卡尔积和性能崩盘。比如判断某天销售额是否首次跌破阈值、用户连续 3 天登录中断、订单状态从“已发货”跳变到“已签收”——这些都依赖相邻行的值对比,LAG/LEAD 是最轻量、最可读的解法。
LAG 和 LEAD 的参数陷阱:排序必须显式指定
常见错误是只写 LAG(sales) 却没配 ORDER BY,结果返回随机行的前值,转折点全错。窗口函数不保证物理顺序,只按 OVER 子句里的排序逻辑取值。
正确写法必须带明确排序:
SELECT dt, sales, LAG(sales) OVER (ORDER BY dt) AS prev_sales, LEAD(sales) OVER (ORDER BY dt) AS next_sales FROM daily_sales;
如果分析维度是用户+时间,排序要写成 ORDER BY user_id, dt;漏掉 user_id 就会跨用户拉数据,把张三昨天和李四今天拼一起算“转折”。
用布尔差值定位真实转折点(不是所有变化都算)
单纯看 sales != LAG(sales) 会把所有波动都当转折,但业务关心的是方向性突变:比如连续上涨后首次下跌、或低于均值后首次回升。这时得组合条件:
- 下跌转折点:
sales (确认前值不比前前值更小) - 突破阈值点:
sales >= 10000 AND LAG(sales) - 状态跃迁(如订单):
status = 'completed' AND LAG(status) = 'shipped'
注意 LAG(col, n) 的第二个参数是偏移量,默认为 1;需要隔两行比较时别硬写两层嵌套 LAG(LAG(x)),直接用 LAG(x, 2) 更稳。
NULL 边界处理:首尾行天然缺失参照,别让它污染结果
LAG 在第一行返回 NULL,LEAD 在最后一行也返回 NULL。如果用 WHERE sales > LAG(sales),NULL 参与比较结果是 UNKNOWN,这行会被过滤掉——看起来像“自动跳过边界”,实则掩盖了首日/末日可能存在的异常(比如第一天就暴跌)。
稳妥做法是显式处理:
SELECT dt, sales
FROM (
SELECT
dt,
sales,
LAG(sales) OVER (ORDER BY dt) AS prev_sales,
CASE
WHEN LAG(sales) IS NULL THEN 'first_day'
WHEN sales > LAG(sales) THEN 'up'
WHEN sales
<p>业务上真正的“转折”往往藏在数据断层处,比如系统上线首日无历史值,但那天本身可能是策略切换起点——这时候 NULL 不是噪音,是信号。</p>










