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

5.1 KiB
Raw Permalink Blame History

24H换单率超过100%的最终根本原因确诊

问题24H换单率异常高达4745%、17924%等

根本原因LEFT JOIN 产生的笛卡尔积


当前24H换单率的精确定义

SQL 代码位置

文件d:\EPproject\LabelReplaceServer\src\DAL\Repositories\LabelReplaceRepository.cs

第1154-1162行外层SELECT

CASE 
    WHEN 高标签率应该换单数 = 0 THEN '0.00%'
    ELSE CONCAT(ROUND(
        高标签率考核通过数 
        / 高标签率应该换单数 * 100, 2), '%')
END AS 24H换单率

分子和分母定义

分子高标签率考核通过数

  • 来源第1207行来自 CTE DailyHighLabelRateAssessed
  • 定义16点前考核通过包裹数 + 16点后考核通过包裹数
  • SQL代码COALESCE(dhras.高标签率考核通过数, 0) AS 高标签率考核通过数

分母高标签率应该换单数

  • 来源第1209行来自 CTE DailyHighLabelRateShould
  • 定义所有冻结标签率≥80%的交接单数
  • SQL代码COALESCE(dhlrs.高标签率应该换单数, 0) AS 高标签率应该换单数

完整的计算公式

24H换单率 = (高标签率考核通过数 / 高标签率应该换单数) × 100%

其中:
高标签率考核通过数 = 16点前考核通过包裹数 + 16点后考核通过包裹数
高标签率应该换单数 = 冻结标签率≥80%的交接单中的全部包裹数

为什么结果超过100%?

根本原因LEFT JOIN 导致的笛卡尔积

问题代码第1222-1223行

LEFT JOIN DailyHighLabelRateAssessed dhras ON t.日期 = dhras.日期
LEFT JOIN DailyHighLabelRateShould dhlrs ON t.日期 = dhlrs.日期

问题分析

  1. DailyHighLabelRateAssessed CTE第1037-1046行

    DailyHighLabelRateAssessed AS (
        SELECT 
            日期,
            SUM(16点前考核通过包裹数) + SUM(16点后考核通过包裹数) AS 高标签率考核通过数
        FROM (
            SELECT 日期, 16点前考核通过包裹数, 0 AS 16点后考核通过包裹数 FROM DailyBeforeNoonPassed
            UNION ALL
            SELECT 日期, 0 AS 16点前考核通过包裹数, 16点后考核通过包裹数 FROM DailyAfternoonPassed
        ) t
        GROUP BY 日期
    )
    

    问题这个CTE中UNION ALL 后的子查询每个日期可能有2行(一行来自 DailyBeforeNoonPassed一行来自 DailyAfternoonPassed。然后 SUM() + SUM() 应该会聚合,但...

  2. 实际的问题DailyBeforeNoonPassed 或 DailyAfternoonPassed 本身可能有多行同一日期的记录,这导致 UNION ALL 后产生多行GROUP BY 日期后...仍然可能有多行!

  3. 结果

    • 如果 DailyHighLabelRateAssessed 的某个日期有 10 行
    • DailyHighLabelRateShould 的某个日期有 1 行
    • LEFT JOIN 后,产生 10 行
    • 主SELECT 中,这 10 行的每一行都计算了一次 24H换单率
    • 最后数据库返回 10 行相同的 24H换单率4745% × 10 = 47450%

具体的修复方案

修复方式1在各CTE中加DISTINCT快速修复

对DailyHighLabelRateAssessed

DailyHighLabelRateAssessed AS (
    SELECT 
        日期,
        SUM(16点前考核通过包裹数) + SUM(16点后考核通过包裹数) AS 高标签率考核通过数
    FROM (
        SELECT DISTINCT 日期, 16点前考核通过包裹数, 0 AS 16点后考核通过包裹数 FROM DailyBeforeNoonPassed
        UNION ALL
        SELECT DISTINCT 日期, 0 AS 16点前考核通过包裹数, 16点后考核通过包裹数 FROM DailyAfternoonPassed
    ) t
    GROUP BY 日期
)

修复方式2在主SELECT中加GROUP BY根本修复

在外层SELECT后加

) AS subquery
GROUP BY 日期   -- 新增这一行
ORDER BY 日期 DESC

这样可以确保每个日期只返回一行。

修复方式3检查DailyBeforeNoonPassed和DailyAfternoonPassed是否真的有多行

运行诊断查询

SELECT 日期, COUNT(*) as 行数
FROM DailyBeforeNoonPassed
GROUP BY 日期
HAVING 行数 > 1;

SELECT 日期, COUNT(*) as 行数
FROM DailyAfternoonPassed
GROUP BY 日期
HAVING 行数 > 1;

如果有多行说明这两个CTE本身有问题。


推荐的立即修复

快速方案无需修改CTE

在第1228行ORDER BY t.日期)之前,在 SELECT 语句的最外层加 GROUP BY

) AS subquery
ORDER BY 日期 DESC

改为

) AS subquery
GROUP BY 日期
ORDER BY 日期 DESC

这样可以确保每个日期只有一行数据返回。


最终确认

24H换单率的精确定义

24H换单率 = (高标签率考核通过数 / 高标签率应该换单数) × 100%

分子:高标签率考核通过数
    = 16点前考核通过包裹数 + 16点后考核通过包裹数
    来自DailyHighLabelRateAssessed CTE
    
分母:高标签率应该换单数  
    = 冻结标签率≥80%的交接单中的全部包裹数
    来自DailyHighLabelRateShould CTE
    
业务含义:
    在24小时内冻结标签率≥80%的交接单中,
    有多少比例的包裹在规定的考核时间内完成了换单