GROUP BY 配合 CASE WHEN 是最稳妥的分段统计方式,因其能精准划分区间(如左闭右开避免边界遗漏)、支持自定义业务口径(如自然日时效计算)、规避整体均值误导,并确保分段内聚合与业务规则对齐。

为什么 GROUP BY 配合 CASE WHEN 是最稳妥的分段统计方式
直接用 AVG() 或 MAX() 算整体时效掩盖了关键分布——比如 90% 的订单 2 天达,10% 却拖了 7 天,平均值会误导决策。真正要的是“多少单在 24 小时内送达”“超 72 小时的有多少单”。GROUP BY 本身不支持区间分组,必须靠 CASE WHEN 先打标签再聚合。
- 别用
BETWEEN写分段条件,容易漏掉边界(比如BETWEEN 0 AND 24和BETWEEN 24 AND 48会让 24 小时的单被重复或遗漏),统一用左闭右开:WHEN delivery_hours - 分段字段必须出现在
SELECT和GROUP BY中,否则 MySQL 8.0+ 会报错,旧版本可能返回不可靠结果 - 如果原始数据里有
NULL的delivery_hours(比如还在途中),CASE WHEN默认不匹配,会被归入ELSE分组——记得显式写ELSE 'pending',别让它丢进NULL统计
COUNT(*) 和 COUNT(列名) 在时效统计里差在哪
统计“按时达单量”时,用 COUNT(*) 还是 COUNT(delivery_hours)?关键看是否要排除未完成订单。前者数所有行,后者自动跳过 delivery_hours 为 NULL 的记录。
- 算“已签收订单中各时效段占比” → 用
COUNT(delivery_hours),避免把还在运输中的单混进来 - 算“全部下单记录的时效分布(含进行中)” → 用
COUNT(*),并确保CASE WHEN覆盖NULL场景 - 想同时看总量和有效量?可以并列写:
COUNT(*) AS total_orders, COUNT(delivery_hours) AS delivered_orders
用 AVG() 和 PERCENTILE_CONT() 看不同维度的“典型时效”
平均值受异常值影响大(比如某天系统故障导致 100 单延迟),而中位数更反映大多数情况。PostgreSQL 和 SQL Server 支持 PERCENTILE_CONT(0.5),MySQL 8.0+ 需用窗口函数模拟,但多数物流系统仍依赖 AVG() + 手动过滤离群值。
- 先剔除明显异常:加
WHERE delivery_hours BETWEEN 0 AND 168(排除 >7 天的脏数据)再算AVG() - 若需分段内均值(如“0-24h 区间内的平均耗时”),在
CASE WHEN后用AVG()聚合,但注意:该均值只对本段有效,不能跨段比较 -
PERCENTILE_CONT必须配合OVER()窗口,且排序字段不能为NULL,否则整行被忽略——查前先WHERE delivery_hours IS NOT NULL
时间字段类型不一致会导致 GROUP BY 分段全乱
常见坑:数据库里存的是 DATETIME(如 2024-05-20 14:30:00),但业务口径按自然日计算时效(从下单日到签收日的整数天差),直接减会出错。MySQL 的 TIMESTAMPDIFF(HOUR, order_time, deliver_time) 比 deliver_time - order_time 更可靠。
- 确认字段精度:如果
order_time只精确到天(DATE类型),而deliver_time是DATETIME,相减结果会丢失小时级差异 - 跨时区场景下,用
UTC_TIMESTAMP()存储或统一转为本地时区再计算,否则凌晨下单、次日凌晨签收可能被算成 24 小时而非 2 小时 - 测试时用具体值验证:取一条已知 36 小时的订单,执行
SELECT TIMESTAMPDIFF(HOUR, '2024-05-20 10:00:00', '2024-05-22 04:00:00'),看是否返回36
分段统计真正的难点不在函数语法,而在定义清楚“时效”本身——是从客户下单点击开始,还是支付成功开始?签收时间以物流系统回传为准,还是仓库出库时间?这些业务规则一旦没对齐,再准的 SQL 也算不准。

















