MTBF必须用总运行时间÷故障次数计算,不能直接对故障间隔取AVG();正确方法需基于带运行状态或启停时间的完整数据,仅故障时间戳只能得出不可靠的估算值。
直接用 AVG() 算不出真正可用的 MTBF —— 因为 MTBF 不是“故障间隔时间的平均值”,而是“总运行时间 ÷ 故障次数”,且必须排除停机/维修时间、计划维护时段和未发生故障的截尾数据。
常见错误是把每次故障之间的时间差(比如用 LAG() 算出相邻故障时间戳之差)直接套 AVG()。这会高估 MTBF:它隐含假设设备从第一次启动到最后一故障始终在运行,忽略了中间停机、待机、计划保养等非工作时间。
正确做法是明确区分「实际累计运行小时数」和「日历时间」。如果你的日志表只记录故障时间点(fault_time),没有运行状态标记,就无法反推出真实运行时长 —— 此时计算结果只是近似值,不能用于可靠性报告或质保承诺。
start_time 和 end_time 的运行周期表,或带 status('running'/'idle'/'maintenance')的连续状态日志COUNT(*) = 0),MTBF = NULL 是合理结果,不应硬补 0 或极大值假设你有一张 equipment_runs 表,每条记录代表一段连续运行(run_id, start_ts, end_ts, equipment_id),另一张 faults 表记录真实故障(fault_id, fault_time, equipment_id),且所有故障都发生在某次运行周期内:
SELECT e.equipment_id, SUM(EXTRACT(EPOCH FROM (e.end_ts - e.start_ts)) / 3600) AS total_operating_hours, COUNT(f.fault_id) AS fault_count, CASE WHEN COUNT(f.fault_id) > 0 THEN SUM(EXTRACT(EPOCH FROM (e.end_ts - e.start_ts)) / 3600) / COUNT(f.fault_id) ELSE NULL END AS mtbf_hoursFROM equipment_runs eLEFT JOIN faults f ON f.equipment_id = e.equipment_id AND f.fault_time BETWEEN e.start_ts AND e.end_tsGROUP BY e.equipment_id;
关键点:
EXTRACT(EPOCH FROM ...) 把 interval 转成秒,再除 3600 得小时 —— 不同数据库写法略有差异:DATE_DIFF('hour', ...)(BigQuery)、strftime('%s', ...)(SQLite)需按引擎调整LEFT JOIN + BETWEEN 确保只统计发生在运行时段内的故障,避免把停机期间的误报计入AVG(DATEDIFF(...)) 替代 —— 它无法处理跨多周期的故障归因如果只有 faults 表(含 fault_time, equipment_id),唯一能做的就是用首末故障时间粗略估计“最小可能运行时间”:
SELECT equipment_id, (EXTRACT(EPOCH FROM MAX(fault_time)) - EXTRACT(EPOCH FROM MIN(fault_time))) / 3600 AS elapsed_hours, COUNT(*) AS fault_count, (EXTRACT(EPOCH FROM MAX(fault_time)) - EXTRACT(EPOCH FROM MIN(fault_time))) / 3600 / NULLIF(COUNT(*), 0) AS mtbf_estimateFROM faultsGROUP BY equipment_id;
这个结果叫 mtbf_estimate,不是 MTBF。它隐含两个强假设:
工程上,这种估算仅适用于快速筛查(如对比不同产线同类设备的相对稳定性),不可用于 FMEA 或可靠性验证报告。
真正影响 MTBF 数值可信度的,从来不是 SQL 写得够不够炫,而是底层数据是否记录了设备“何时真正在干活”。没运行状态标记的故障日志,就像只有病历没有体检报告,算出来的数字看着整齐,但没法指导备件策略或设计改进。