“连续3个月业绩下滑”指同一门店任意连续三个月销售额严格递减(month1 > month2 > month3),需用lag()配合partition by store_id order by sales_month生成prev1_sales和prev2_sales,并确保sales_month为规范日期格式以避免排序错误。

什么是“连续3个月业绩下滑”的准确逻辑
这不是简单查出某月比上月低,而是要确认一个门店在任意连续三个月内,每月销售额都严格小于前一个月(即 month1 > month2 > month3)。窗口函数必须能访问当前行及前两行的数据,且需按门店+时间严格排序,否则 LAG() 会跨门店取值或打乱时序。
常见错误是只用 LAG(sales, 1) 和 LAG(sales, 2) 却没加 PARTITION BY store_id ORDER BY sales_month —— 这会导致不同门店的记录混在一起计算,结果完全不可信。
用 LAG() 构造三阶比较字段
核心是为每条记录生成两个可比字段:上月销售额、上上月销售额。必须在同一 PARTITION BY store_id 内按月份升序排列,否则 LAG() 返回的是错位值。
SELECT store_id, sales_month, sales, LAG(sales, 1) OVER (PARTITION BY store_id ORDER BY sales_month) AS prev1_sales, LAG(sales, 2) OVER (PARTITION BY store_id ORDER BY sales_month) AS prev2_sales FROM monthly_sales;
注意:sales_month 必须是 DATE 或可排序的规范格式(如 '2024-01-01'),不能是字符串 '202401'——否则 ORDER BY 可能按字典序排错(比如 '202410' 排在 '202402' 前面)。
筛选下滑三连并避免边界误判
有了三阶字段后,直接用 WHERE 判断严格递减关系即可,但要注意 NULL 处理和边界:
-
prev1_sales和prev2_sales在首月、次月必然为 NULL,这些行天然不满足三连条件,无需额外过滤 - 必须写成
sales ,不能写成 <code>sales (SQL 不支持链式比较) - 如果存在同月多条记录或销售为 0/NULL,需提前用
WHERE sales IS NOT NULL排除干扰
完整筛选语句示例:
WITH ranked AS (
SELECT
store_id,
sales_month,
sales,
LAG(sales, 1) OVER (PARTITION BY store_id ORDER BY sales_month) AS prev1_sales,
LAG(sales, 2) OVER (PARTITION BY store_id ORDER BY sales_month) AS prev2_sales
FROM monthly_sales
WHERE sales IS NOT NULL
)
SELECT store_id, sales_month
FROM ranked
WHERE sales
<h3>性能与数据质量陷阱</h3>
<p>当门店数或月份数很大时,<code>PARTITION BY store_id ORDER BY sales_month</code> 的开销取决于索引是否覆盖。务必确保表上有 <code>(store_id, sales_month)</code> 联合索引,否则全表排序会极慢。</p>
<p>更隐蔽的问题是「连续」的定义依赖于数据完整性:如果某门店缺了 2024-03 的记录,那么 2024-02 / 2024-04 / 2024-05 就不算连续三个月——<code>LAG()</code> 只认物理顺序,不补缺失月。真要严格按日历连续,得先用 <code>GENERATE_SERIES</code>(PostgreSQL)或递归 CTE 补全月份,再左连接销售数据,代价高得多。</p>










