窗口函数计算工序等待时间的核心是用lag()获取同订单同产线的上一道工序结束时间,再与当前工序开始时间相减;必须按业务序号(如step_id或exec_seq)排序,而非时间字段,并完整分区partition by order_id, line_id。

窗口函数怎么算工序间的等待时间
核心是用 LAG() 拿到上一道工序的结束时间,再和当前工序的开始时间相减。等待时间 = 当前工序 start_time − 上一道工序 end_time,前提是两道工序属于同一订单、同一产线,且按实际执行顺序排序。
常见错误是直接用 ORDER BY start_time 排序,但实际工序执行可能乱序(比如返工、插单),必须按 process_order 或 step_id 这类业务序号排序,否则 LAG() 拉出来的不是前一道工序。
- 确保分区字段写全:
PARTITION BY order_id, line_id,漏掉line_id会导致跨产线混算 -
LAG(end_time) OVER (PARTITION BY order_id, line_id ORDER BY step_id)中ORDER BY必须用稳定、业务定义的序号,不用时间字段 - 第一道工序没有“上一道”,
LAG()返回NULL,需用COALESCE(..., start_time)或直接留空,别硬填 0
如何处理同一工序多次执行(如返工)
返工会让同一 step_id 出现多次,这时仅靠 step_id 排序会出错——第二次返工的“前一道”不该是第一次的同工序,而应是它上一次执行的任意工序。解决方案是引入唯一执行序号:
用 ROW_NUMBER() OVER (PARTITION BY order_id, line_id ORDER BY actual_timestamp) 生成 exec_seq,再以此替代 step_id 做 LAG() 的排序依据。
- 不能依赖原始
step_id,返工时该值不变,但执行逻辑上已非线性 -
actual_timestamp要是真实记录设备触发或人工确认的时间,不是计划时间 - 若无精确时间戳,至少用带自增 ID 的日志表主键,比用
step_id更可靠
等待时间为负值说明什么
算出来 wait_time = start_time - LAG(end_time) 是负数,代表后一道工序在前一道还没结束时就已开始——典型重叠执行(如并行测试、交叉装配)。这不是计算错误,而是真实生产现象。
是否要过滤或告警,取决于业务规则:有的允许重叠(如质检与包装同步),有的则视为异常(如热处理未冷却就进机加)。别一看到负值就 ABS() 或设为 0。
- 保留负值,加一列
is_overlap标记:CASE WHEN wait_time - 统计时分开聚合:
AVG(NULLIF(wait_time, wait_time * (wait_time 可排除重叠项(PostgreSQL/MySQL 8.0+ 支持) - 注意时区一致性:所有
start_time/end_time必须同属一个时区,数据库和应用层不要混用 UTC 与本地时间
性能要注意哪些地方
窗口函数本身不慢,但数据量大时,PARTITION BY + ORDER BY 的排序成本高。尤其当订单数超百万、每单工序超 50 步时,没索引会明显拖慢。
关键索引组合必须覆盖: (order_id, line_id, step_id) 或 (order_id, line_id, exec_seq)。只建 (order_id) 单字段索引没用。
- 避免在窗口函数里嵌套复杂表达式,比如
LAG(DATE_ADD(end_time, INTERVAL 10 SECOND)),先算好字段再引用 - 如果只查最近 7 天数据,务必在
WHERE子句里加时间过滤,别让窗口函数扫全表 - PostgreSQL 中
frame_clause默认是RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,对时间字段易触发排序;明确写成ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW更稳
真实产线数据往往带噪声:时间戳精度不一致、工序漏报、设备时钟漂移。窗口函数给出的是“按数据算出的结果”,不是“物理等待”,得结合现场校验才能下结论。











