窗口函数是生成标签所需统计特征的“原材料工厂”,能产出“近7日活跃天数”等中间特征;GROUP BY 会压缩行数丢失时间细节,而窗口函数通过 ROW_NUMBER() 等保留明细并排序。

为什么不能用 GROUP BY 做用户行为聚合?
GROUP BY 会压缩行数,一压就丢时间细节。比如想算“每个用户最近3次订单的平均金额”,GROUP BY user_id 后你只剩一个均值,根本不知道哪三笔是“最近”的,也无法判断时间先后。
窗口函数保留每条行为明细,靠 ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY order_time DESC) 先打倒序号,再用 WHERE rn 精准圈出目标行——这才是可控、可验证的路径。
- 错误写法:
SELECT user_id, AVG(amount) FROM orders GROUP BY user_id→ 无时间维度,无法定义“最近” - 正确路径:加序号 → 过滤 → 再聚合(可在子查询或 CTE 中完成)
- 注意:
OVER()只能在SELECT和ORDER BY中出现,不能放WHERE或HAVING里
怎么算“7日留存率”这类跨时间点指标?
留存率本质是“某日新增用户中,后续7日内再次活跃的比例”,必须关联不同日期的行为行。这时 LEAD() 或 LAG() 是关键。
典型做法:先按 user_id 和 event_date 排序 → 用 LEAD(event_date, 1) OVER (PARTITION BY user_id ORDER BY event_date) 拿到下次活跃日 → 判断是否 ≤ 当前日 + 7 → 转成 0/1 标识 → 最后按注册日 GROUP BY 统计比例。
- 若需“7日内任意一天活跃”(非仅下一次),得配合
MIN(event_date) OVER (PARTITION BY user_id ORDER BY event_date ROWS BETWEEN CURRENT ROW AND 6 FOLLOWING),但注意旧版 MySQL 不支持ROWS BETWEEN,得用自连接模拟,性能差且易漏数据 -
event_date必须去重(如取MIN(event_date)),否则单日多行为会导致同一用户被重复计入分母
累计类和移动类特征怎么选 ORDER BY 和 PARTITION BY?
业务语义决定结构:要对比组内水平,必须 PARTITION BY;要体现趋势或顺序依赖,必须 ORDER BY;两者常共存,但顺序不能反。
例如“用户累计消费额”:SUM(amount) OVER (PARTITION BY user_id ORDER BY order_time) —— 每个用户独立累计,且严格按时间升序;如果漏掉 ORDER BY,结果就是该用户全部金额的重复填充,失去时序意义。
- 移动平均(如近7日):
AVG(amount) OVER (PARTITION BY user_id ORDER BY order_time ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) - 错配风险:只
PARTITION BY不ORDER BY→ 累计类函数失效;只ORDER BY不PARTITION BY→ 所有用户混在一起排序,排名或累计完全失真

















