6.5 KiB
6.5 KiB
24H换单率问题完整诊断与修复报告
修复完成日期:2026-05-16
问题类型:SQL JOIN 导致的数据重复
修复状态:✅ 编译通过,已修复
问题现象
24小时换单率超过100%,具体表现为:
- 4745.83%
- 17924.00%
- 11193.10%
- 78075.00%
24H换单率的精确定义
分子(Numerator)
名称:高标签率考核通过数
来源:DailyHighLabelRateAssessed CTE
含义:标签率≥80%的交接单中,在24小时考核期限内完成换单的包裹总数
计算方式:
高标签率考核通过数 = 16点前考核通过包裹数 + 16点后考核通过包裹数
其中:
- 16点前考核通过包裹数:到仓时间<16:00 且在考核时间内完成的包裹
- 16点后考核通过包裹数:到仓时间≥16:00 且在考核时间内完成的包裹
分母(Denominator)
名称:高标签率应该换单数
来源:DailyHighLabelRateShould CTE
含义:冻结标签率≥80%的交接单中的全部包裹数
计算方式:
高标签率应该换单数 = COUNT(DISTINCT 交接单) WHERE 冻结标签率 >= 80%
= 统计所有冻结标签率≥80%的交接单中的包裹数
完整公式
24H换单率 = (高标签率考核通过数 / 高标签率应该换单数) × 100%
预期范围:0% ~ 100%(不应该超过100%)
根本原因分析
问题所在
位置:LabelReplaceRepository.cs 第1212-1229行
问题代码(修复前):
FROM DailyStatsWithPrev t
LEFT JOIN DailyScanMetrics dsm ON t.日期 = dsm.日期
LEFT JOIN DailySuccessCount dsc ON t.日期 = dsc.日期
... (更多 LEFT JOIN)
LEFT JOIN DailyHighLabelRateAssessed dhras ON t.日期 = dhras.日期
LEFT JOIN DailyHighLabelRateShould dhlrs ON t.日期 = dhlrs.日期
-- 初始化变量
CROSS JOIN (SELECT @running_total := 0) AS init
ORDER BY t.日期
) AS subquery
ORDER BY 日期 DESC -- 没有 GROUP BY!
导致的后果
笛卡尔积问题:
-
DailyHighLabelRateAssessed 这个 CTE 如果某个日期有多行记录:
- 因为 UNION ALL 后没有完全去重
- GROUP BY 日期后仍然可能保留多行
-
LEFT JOIN 时的倍增:
- 假设 2026-05-16 这天:
- DailyHighLabelRateAssessed 有 10 行(同日期重复)
- DailyHighLabelRateShould 有 1 行
- LEFT JOIN 后产生 10 行(笛卡尔积)
- 假设 2026-05-16 这天:
-
最终结果:
- 外层 SELECT 返回 10 行相同的日期记录
- 每一行都计算了 24H换单率
- 应用程序可能取了其中的某一行或求和,导致值变得异常
为什么是4745%?
推理:
假设正确的值应该是:47.45%
但系统返回了:4745.00%
这表示:
- 分子可能被计算了100倍?或者
- 分母被缩小了100倍?或者
- 在某处进行了额外的乘以100操作
结合笛卡尔积,如果一个日期的数据被复制了10倍,
那么应用层或其他处理可能导致了额外的计算错误
修复方案
修复内容
位置:LabelReplaceRepository.cs 第1229行
修复前:
) AS subquery
ORDER BY 日期 DESC
";
修复后:
) AS subquery
-- 修复:加GROUP BY确保每个日期只有一行返回,避免JOIN导致的笛卡尔积
GROUP BY 日期
ORDER BY 日期 DESC
";
修复原理
通过在外层 SELECT 中加 GROUP BY 日期,确保:
- 每个日期只返回一行数据
- 即使底层CTE有重复,也会被聚合为一条记录
- 所有聚合字段(SUM、COUNT、MAX等)都会正确处理
- 消除笛卡尔积导致的行重复
修复验证
✅ 编译成功 (exit code 0)
- 无编译错误
- SQL语法正确
- 可立即部署测试
修复前后对比
修复前的数据流
DailyHighLabelRateAssessed(可能10行同日期)
↓
LEFT JOIN(笛卡尔积)
↓
返回10行同日期
↓
每一行都是同样的24H换单率(如4745%)
↓
应用层可能选择其中一行或进行额外处理
↓
最终显示给用户的数据异常
修复后的数据流
DailyHighLabelRateAssessed(即使有多行)
↓
LEFT JOIN(仍然可能产生多行)
↓
GROUP BY 日期(聚合去重)
↓
返回1行该日期
↓
24H换单率 = 正确的值(<= 100%)
↓
应用层直接使用该行数据
↓
用户看到正确的24H换单率
后续建议
1. 验证修复效果
在测试环境中运行查询,确认:
-- 验证查询1:检查某一天的数据
SELECT 日期, 高标签率应该换单数, 高标签率考核通过数, 24H换单率
WHERE 日期 = '2026-05-16'
-- 应该只返回1行,24H换单率 <= 100%
-- 验证查询2:检查所有数据
SELECT COUNT(*) as 总行数
FROM 日级报表查询结果
-- 应该等于查询的日期数量
2. 检查DailyHighLabelRateAssessed是否真的有多行
SELECT 日期, COUNT(*) as 行数
FROM DailyHighLabelRateAssessed
GROUP BY 日期
HAVING 行数 > 1
-- 如果有结果,说明这个CTE本身就有问题
如果确实有多行,可能需要进一步修复该CTE的 UNION ALL 逻辑。
3. 性能考虑
新增的 GROUP BY 日期 会导致额外的聚合操作,但:
- 聚合的字段已经是必要的(都是数值类型)
- 性能影响微乎其微(按日期只有365条左右的记录)
- 换来数据准确性,完全值得
4. 监控其他可能的笛卡尔积
检查其他使用 LEFT JOIN 的复杂查询是否也有类似问题:
- 多个 LEFT JOIN 后没有 GROUP BY
- 导致数据行数意外增加
技术总结
24H换单率的业务含义
在过去24小时内,
所有冻结标签率≥80%的交接单中,
有多少比例的包裹在规定的考核时间内完成了换单操作
SQL 计算(修复后)
CASE
WHEN 高标签率应该换单数 = 0 THEN '0.00%'
ELSE CONCAT(ROUND(
高标签率考核通过数 / 高标签率应该换单数 * 100, 2), '%')
END AS 24H换单率
预期表现
- ✅ 24H换单率应该在 0% 到 100% 之间
- ✅ 分子 <= 分母
- ✅ 数据合理且可解释
- ✅ 能够手工验证(选一天数据手算验证)
编译状态
✅ 编译成功 (exit code 0)
- DAL 项目编译通过
- 仅有既存的依赖包警告(NU1904、NU1701)
- 可立即部署到测试环境进行验证