LAG()函数通过OVER(PARTITION BY ticket_id ORDER BY handled_at)精准获取上一环节处理时间,必须加唯一字段防并发重复,并用子查询或CTE在WHERE外处理超时判断,首环节需过滤默认值,负值和节假日等业务逻辑须额外处理。

LAG函数怎么取上一条工单的处理时间
直接用 LAG() 提前拿到上一环节的完成时间,是判断超时流转最常用也最可靠的方式。关键不是“能不能用”,而是“用对了没”——比如没按工单ID和处理时间双重排序,结果取到的是其他工单的时间,超时判定全错。
实操要点:
- 必须在
OVER子句里写ORDER BY ticket_id, handled_at:先按工单分组,再按时间升序,否则LAG(handled_at)可能跨工单取值 - 如果原始数据里存在同一工单多条记录但
handled_at相同(比如并发写入),要加唯一字段兜底,例如ORDER BY ticket_id, handled_at, id -
LAG(handled_at, 1, '1970-01-01')中的默认值别设NULL,否则后续计算current_time - lag_time会整体变NULL
怎么算每个环节是否超时(含SLA规则)
超时不是简单比大小,得结合环节类型查SLA配置表。比如“技术审核”环节SLA是2小时,“客户确认”是24小时,硬编码在SQL里迟早出问题。
推荐做法:
- 把SLA规则单独建表
sla_rules(stage_name, max_hours),JOIN 到主查询里 - 用
EXTRACT(EPOCH FROM (current_handled_at - prev_handled_at)) / 3600.0算出真实耗时(单位:小时),避免用AGE()或字符串相减导致时区或精度偏差 - 注意 PostgreSQL 的
LAG()返回TIMESTAMP WITH TIME ZONE,若字段是timestamp without time zone,先显式转成带时区再相减,否则可能跨日出错
为什么WHERE条件不能直接写“超时 > SLA”
因为 LAG() 是窗口函数,不能出现在 WHERE 子句里——这是初学者最常卡住的地方,报错 ERROR: window functions are not allowed in WHERE。
正确解法只有两个:
- 套一层子查询:
SELECT * FROM (SELECT ..., LAG(...) OVER (...) AS prev_time FROM tickets) t WHERE (handled_at - prev_time) > INTERVAL '2 hours' - 或用 CTE:
WITH w AS (SELECT *, LAG(handled_at) OVER (PARTITION BY ticket_id ORDER BY handled_at) AS prev_handled_at FROM tickets) SELECT * FROM w WHERE ... - 别试图用
HAVING替代——它只对聚合有效,这里没GROUP BY
真实工单流中容易漏掉的边界情况
生产环境里,真正让超时检测失效的往往不是语法,而是业务逻辑盲区。
- 首环节无上一节点:
LAG()返回默认值,但这个“无前置”本身不等于超时,需用prev_handled_at != '1970-01-01'过滤掉首行 - 环节跳转非线性:比如工单从“审核中”被打回“待提交”,时间戳反而变早,此时
handled_at - prev_handled_at为负,得加CASE WHEN handled_at > prev_handled_at THEN ... ELSE 0 END - 节假日/非工作时间要排除:单纯算时间差会误判,真要严谨就得引入工作日历表,用自定义函数算“有效工时”,这已超出
LAG()能力范围
越想覆盖所有场景,越得承认:LAG只是帮你拿到“上一时刻”,剩下的判断逻辑,得靠业务理解来补全。










