LAG函数必须配合PARTITION BY task_id和ORDER BY event_time, id,且时间字段须为TIMESTAMP/DATETIME类型;阶段耗时需用各库专用函数计算,不可直接相减;WHERE中不能直接使用LAG,须通过子查询或CTE先计算再过滤。

LAG函数必须配合PARTITION BY和ORDER BY才能算对阶段耗时
直接写LAG(event_time)不加分区和排序,结果全是跨任务、跨用户的错乱值。比如订单A的“支付完成”时间,可能被拿来减订单B的“下单时间”,差值毫无业务意义。
正确写法必须同时满足三点:
-
PARTITION BY task_id:确保只在同一个业务实例内比较 -
ORDER BY event_time, id:时间相同时用自增id保序,避免窗口行为不稳定 - 字段类型要是真正的
TIMESTAMP或DATETIME,别用字符串存时间——查pg_typeof(event_time)(PG)或DESCRIBE table_name(MySQL)确认
时间差计算不能直接相减,要按数据库选函数
写next_event_time - event_time看着简单,但一跑就报错或返回0,大概率是数据库类型没对上。
各库安全写法:
- PostgreSQL:
EXTRACT(EPOCH FROM (next_event_time - event_time))::INT→ 得到秒数 - MySQL:
TIMESTAMPDIFF(SECOND, event_time, next_event_time)→ 单位必须显式写,且顺序不能反 - SQL Server:
DATEDIFF(second, event_time, next_event_time)→ 第一个参数是单位,第二个是起点 - BigQuery:
TIMESTAMP_DIFF(next_event_time, event_time, SECOND)→ 第三个参数大小写敏感
别用COALESCE(LEAD(), NOW())填NULL——未完结阶段强行按“现在”算,会把平均耗时拉高几倍。
WHERE里不能直接用LAG(),得套一层子查询或CTE
很多人写WHERE LAG(event_time) IS NOT NULL AND next_event_time > event_time,直接报语法错误。因为窗口函数执行晚于WHERE,解析器根本认不出。
必须先算出next_event_time,再在外层过滤:
SELECT *
FROM (
SELECT task_id, stage, event_time,
LEAD(event_time) OVER (PARTITION BY task_id ORDER BY event_time, id) AS next_event_time
FROM task_events
) t
WHERE next_event_time IS NOT NULL AND next_event_time > event_time;注意子查询必须加别名t,否则MySQL会报Every derived table must have its own alias。
阶段耗时为NULL或负值,说明数据本身有问题
如果大量出现NULL,常见原因是某任务只有一条事件记录(比如只到“创建”就中断了),LEAD()自然返回NULL;如果出现负值,基本可断定数据乱序或人工补录导致后一事件时间早于前一事件。
别急着用CASE WHEN ... ELSE 0掩盖问题,先查这些:
- 是否存在
event_time为未来时间(如'2099-01-01')? - 同一
task_id下是否有重复event_time且没加次级排序字段? - 是否漏掉状态过滤?比如把
'retry'、'init'这类中间态也参与了耗时计算
真正难处理的不是语法,而是怎么定义“有效阶段”——业务上认为“审核中→审核通过”才算一次完整流转,那'审核中'之后跟了个'已取消',这段耗时就不该计入平均值。

















