# 24H换单率超过100%的最终根本原因确诊 **问题**:24H换单率异常高达4745%、17924%等 **根本原因**:LEFT JOIN 产生的笛卡尔积 --- ## 当前24H换单率的精确定义 ### SQL 代码位置 **文件**:`d:\EPproject\LabelReplaceServer\src\DAL\Repositories\LabelReplaceRepository.cs` **第1154-1162行**(外层SELECT): ```sql 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行): ```sql LEFT JOIN DailyHighLabelRateAssessed dhras ON t.日期 = dhras.日期 LEFT JOIN DailyHighLabelRateShould dhlrs ON t.日期 = dhlrs.日期 ``` **问题分析**: 1. **DailyHighLabelRateAssessed CTE**(第1037-1046行) ```sql 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**: ```sql 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后加**: ```sql ) AS subquery GROUP BY 日期 -- 新增这一行 ORDER BY 日期 DESC ``` 这样可以确保每个日期只返回一行。 ### 修复方式3:检查DailyBeforeNoonPassed和DailyAfternoonPassed是否真的有多行 **运行诊断查询**: ```sql 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: **从**: ```sql ) AS subquery ORDER BY 日期 DESC ``` **改为**: ```sql ) AS subquery GROUP BY 日期 ORDER BY 日期 DESC ``` 这样可以确保每个日期只有一行数据返回。 --- ## 最终确认 **24H换单率的精确定义**: ``` 24H换单率 = (高标签率考核通过数 / 高标签率应该换单数) × 100% 分子:高标签率考核通过数 = 16点前考核通过包裹数 + 16点后考核通过包裹数 来自:DailyHighLabelRateAssessed CTE 分母:高标签率应该换单数 = 冻结标签率≥80%的交接单中的全部包裹数 来自:DailyHighLabelRateShould CTE 业务含义: 在24小时内,冻结标签率≥80%的交接单中, 有多少比例的包裹在规定的考核时间内完成了换单 ```