如何用LAG函数实现SQL查询,找出连续三个月销售增长的所有客户?

2026-04-29 01:312阅读0评论SEO资讯
  • 内容介绍
  • 相关推荐

本文共计842个文字,预计阅读时间需要4分钟。

如何用LAG函数实现SQL查询,找出连续三个月销售增长的所有客户?

直接使用一次LAG函数只能得到上个月的值,无法判断连续三个月增长。必须使用三次LAG函数分别取出前1、2、3个月的销售额,再在WHERE或CASE语句中进行比较。

常见错误是写成 LAG(sales) > LAG(sales, 2) —— 这实际比的是“上月 > 上上月”,漏了“本月 > 上月”这一环,结果会多出大量假阳性。

正确逻辑是:当前月 sales > 上月 AND 上月 > 上上月 AND 上上月 > 上上上月。对应 SQL 要显式写出三个 LAG 列:

SELECT customer_id, order_month, sales, LAG(sales, 1) OVER (PARTITION BY customer_id ORDER BY order_month) AS prev1, LAG(sales, 2) OVER (PARTITION BY customer_id ORDER BY order_month) AS prev2, LAG(sales, 3) OVER (PARTITION BY customer_id ORDER BY order_month) AS prev3 FROM sales_data

为什么 PARTITION BY customer_id 和 ORDER BY order_month 缺一不可?

不加 PARTITION BY customer_idLAG 会在全表范围内错位取值,比如把客户 A 的 3 月值当成客户 B 的 2 月值;不按 ORDER BY order_month 排序,时间顺序乱掉,连续性判断彻底失效。

注意:order_month 必须是可排序的日期类型(如 DATEYYYYMM 整数),不能是字符串格式的 '2024-03' —— 字符串排序下 '2024-10' 会排在 '2024-2' 前面。

如果只有年月字段但存为字符串,先转成日期:TO_DATE(order_month, 'YYYY-MM')(PostgreSQL/Oracle)或 STR_TO_DATE(order_month, '%Y-%m')(MySQL)。

WHERE 条件里怎么安全判断“连续增长”?

LAG 返回 NULL 是常态(首月没前值),所以 WHERE 中必须过滤掉任意一列为 NULL 的行,否则 sales > prev1 会整体返回 UNKNOWN,被 WHERE 当作 FALSE 丢弃——导致本该命中的第4个月数据被漏掉。

实操建议用以下结构:

WHERE sales > prev1 AND prev1 > prev2 AND prev2 > prev3 AND prev1 IS NOT NULL AND prev2 IS NOT NULL AND prev3 IS NOT NULL

更紧凑写法(部分数据库支持):WHERE sales > prev1 AND prev1 > prev2 AND prev2 > prev3 AND prev3 IS NOT NULL —— 因为只要 prev3 非空,前面两个必然非空(LAG 的偏移是递进的)。

性能和边界情况要注意什么?

LAG 窗口函数本身不拖慢查询,但若基表没索引,PARTITION BY customer_id ORDER BY order_month 会触发大量排序,建议在 (customer_id, order_month) 上建联合索引。

容易被忽略的点:

  • 月份必须连续(不能跳月),否则即使数值递增也不算“连续三个月”。例如客户在 1、2、4 月有记录,3 月缺失,LAG 会把 4 月的 prev1 指向 2 月,误判为连续
  • 不同数据库对 LAG(..., n)n 超出范围的处理一致(返回 NULL),但某些旧版 MySQL 不支持第三个参数 default_value,别硬写 LAG(sales, 1, 0)
  • 如果销售值允许为 0 或负数,增长判断仍用 > 即可,无需特殊处理;但若业务定义“增长”指“严格大于 0 的增幅”,需额外加 sales - prev1 > 0

本文共计842个文字,预计阅读时间需要4分钟。

如何用LAG函数实现SQL查询,找出连续三个月销售增长的所有客户?

直接使用一次LAG函数只能得到上个月的值,无法判断连续三个月增长。必须使用三次LAG函数分别取出前1、2、3个月的销售额,再在WHERE或CASE语句中进行比较。

常见错误是写成 LAG(sales) > LAG(sales, 2) —— 这实际比的是“上月 > 上上月”,漏了“本月 > 上月”这一环,结果会多出大量假阳性。

正确逻辑是:当前月 sales > 上月 AND 上月 > 上上月 AND 上上月 > 上上上月。对应 SQL 要显式写出三个 LAG 列:

SELECT customer_id, order_month, sales, LAG(sales, 1) OVER (PARTITION BY customer_id ORDER BY order_month) AS prev1, LAG(sales, 2) OVER (PARTITION BY customer_id ORDER BY order_month) AS prev2, LAG(sales, 3) OVER (PARTITION BY customer_id ORDER BY order_month) AS prev3 FROM sales_data

为什么 PARTITION BY customer_id 和 ORDER BY order_month 缺一不可?

不加 PARTITION BY customer_idLAG 会在全表范围内错位取值,比如把客户 A 的 3 月值当成客户 B 的 2 月值;不按 ORDER BY order_month 排序,时间顺序乱掉,连续性判断彻底失效。

注意:order_month 必须是可排序的日期类型(如 DATEYYYYMM 整数),不能是字符串格式的 '2024-03' —— 字符串排序下 '2024-10' 会排在 '2024-2' 前面。

如果只有年月字段但存为字符串,先转成日期:TO_DATE(order_month, 'YYYY-MM')(PostgreSQL/Oracle)或 STR_TO_DATE(order_month, '%Y-%m')(MySQL)。

WHERE 条件里怎么安全判断“连续增长”?

LAG 返回 NULL 是常态(首月没前值),所以 WHERE 中必须过滤掉任意一列为 NULL 的行,否则 sales > prev1 会整体返回 UNKNOWN,被 WHERE 当作 FALSE 丢弃——导致本该命中的第4个月数据被漏掉。

实操建议用以下结构:

WHERE sales > prev1 AND prev1 > prev2 AND prev2 > prev3 AND prev1 IS NOT NULL AND prev2 IS NOT NULL AND prev3 IS NOT NULL

更紧凑写法(部分数据库支持):WHERE sales > prev1 AND prev1 > prev2 AND prev2 > prev3 AND prev3 IS NOT NULL —— 因为只要 prev3 非空,前面两个必然非空(LAG 的偏移是递进的)。

性能和边界情况要注意什么?

LAG 窗口函数本身不拖慢查询,但若基表没索引,PARTITION BY customer_id ORDER BY order_month 会触发大量排序,建议在 (customer_id, order_month) 上建联合索引。

容易被忽略的点:

  • 月份必须连续(不能跳月),否则即使数值递增也不算“连续三个月”。例如客户在 1、2、4 月有记录,3 月缺失,LAG 会把 4 月的 prev1 指向 2 月,误判为连续
  • 不同数据库对 LAG(..., n)n 超出范围的处理一致(返回 NULL),但某些旧版 MySQL 不支持第三个参数 default_value,别硬写 LAG(sales, 1, 0)
  • 如果销售值允许为 0 或负数,增长判断仍用 > 即可,无需特殊处理;但若业务定义“增长”指“严格大于 0 的增幅”,需额外加 sales - prev1 > 0