Files
LabelChange-server/.trae/documents/24h_rate_complete_solution.md
2026-06-01 16:30:29 +08:00

6.5 KiB
Raw Blame History

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

导致的后果

笛卡尔积问题

  1. DailyHighLabelRateAssessed 这个 CTE 如果某个日期有多行记录:

    • 因为 UNION ALL 后没有完全去重
    • GROUP BY 日期后仍然可能保留多行
  2. LEFT JOIN 时的倍增

    • 假设 2026-05-16 这天:
      • DailyHighLabelRateAssessed 有 10 行(同日期重复)
      • DailyHighLabelRateShould 有 1 行
      • LEFT JOIN 后产生 10 行(笛卡尔积)
  3. 最终结果

    • 外层 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 日期,确保:

  1. 每个日期只返回一行数据
  2. 即使底层CTE有重复也会被聚合为一条记录
  3. 所有聚合字段SUM、COUNT、MAX等都会正确处理
  4. 消除笛卡尔积导致的行重复

修复验证

编译成功 (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
  • 可立即部署到测试环境进行验证