5.1 KiB
5.1 KiB
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.日期
问题分析:
-
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() 应该会聚合,但...
-
实际的问题:DailyBeforeNoonPassed 或 DailyAfternoonPassed 本身可能有多行同一日期的记录,这导致 UNION ALL 后产生多行,GROUP BY 日期后...仍然可能有多行!
-
结果:
- 如果 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%的交接单中,
有多少比例的包裹在规定的考核时间内完成了换单