必须先按天聚合dau再开窗:group by date(event_time) + count(distinct user_id),再对结果用lag(dau) over (order by date);日期须为date类型且连续,否则lag取值错误;需处理null和除零异常。

用 LAG() 获取昨日 DAU 值必须指定正确排序
窗口函数本身不保证顺序,LAG() 默认按物理存储顺序取前一行,这在统计日活时必然出错。必须显式用 ORDER BY date 确保按日期升序排列,否则昨日值可能来自任意一天。
-
LAG(dau)要写成LAG(dau) OVER (ORDER BY date),不能省略ORDER BY - 日期字段必须是
DATE类型或能被准确排序的格式(如'2024-05-20'),避免用字符串'20240520'且未补零导致排序错乱 - 若数据存在缺失日期(比如 5 月 19 日无记录),
LAG()会跳过空缺直接取上一条——即 5 月 18 日的值当作“昨日”,结果失真
计算 DAU 需先聚合再开窗,不能在原始行为表上直接 LAG()
原始日志表通常是一行一用户一事件,直接对未聚合的表使用 LAG() 没有意义:你不是想看“上一条日志的用户 ID”,而是“昨天有多少独立用户”。必须先按天去重计数,再对结果集开窗。
- 错误写法:
LAG(user_id) OVER (ORDER BY event_time)—— 这是在比用户 ID,不是比 DAU - 正确流程:先
GROUP BY DATE(event_time)+COUNT(DISTINCT user_id)得到每日 DAU 表,再在此结果上套LAG(dau) - 示例中间结果应类似:
date | dau<br>-----------+----<br>2024-05-18 | 1240<br>2024-05-19 | 1302<br>2024-05-20 | 1287
对比变化量与变化率时注意 NULL 和除零问题
首日没有“昨日”,LAG() 返回 NULL;若某日 DAU 为 0,算环比增长率会触发除零错误。这些必须显式处理,否则查询报错或结果异常。
- 变化量建议写为:
dau - COALESCE(LAG(dau) OVER (ORDER BY date), 0),首日差值即为当日值 - 变化率更稳妥写法:
ROUND(100.0 * (dau - LAG(dau) OVER (ORDER BY date)) / NULLIF(LAG(dau) OVER (ORDER BY date), 0), 2) -
NULLIF(x, 0)把分母为 0 转成NULL,避免报错;COALESCE或CASE WHEN可进一步把NULL替换为文字如'—'
MySQL 8.0+、PostgreSQL、ClickHouse 支持一致,但 Hive/SparkSQL 有语法差异
核心逻辑通用,但部分引擎对别名引用或函数嵌套有限制。例如 Hive 不允许在 SELECT 中直接用列别名调 LAG(),必须嵌套子查询。
- MySQL/PG/CK:可直接写
SELECT date, dau, dau - LAG(dau) ... - Hive/SparkSQL:需两层查询,外层才能用内层算出的
dau列参与LAG()计算 - 所有引擎中
LAG(dau, 1)的1是默认值,可省略;但若想对比前两天,必须写LAG(dau, 2),不能只改ORDER BY
真实场景里最常被忽略的是日期连续性校验——没人会天天盯着表里有没有断档,但一旦漏了一天,LAG() 就悄无声息地拿错“昨日”数据,且难以排查。










