8.9 KiB
8.9 KiB
SQL 指标优化实现总结(最终版)
实现完成时间
2026-05-16 (最终更新版本)
实现范围确认
✅ 已完成的任务
1. DTO类修改(最终版本)
文件: d:\EPproject\LabelReplaceServer\src\MDL\DTOs\DailyLabelStatsChineseDto.cs
字段调整:
- ❌ 移除:
LabelRate(string) - 系统整体标签率已移除 - ✅ 保留:
BeforeNoonArrivedCount(int) - 16点前到仓的包裹数 - ✅ 保留:
AfternoonArrivedCount(int) - 16点后到仓的包裹数 - ✅ 保留:
ShouldReplaceCount(int) - 当天应该换单数(历史未完成+当日新增) - ✅ 新增:
BeforeNoonPassedCount(int) - 16点前考核通过包裹数 - ✅ 新增:
AfternoonPassedCount(int) - 16点后考核通过包裹数
字段映射正确性: ✅ 所有字段都有 SugarColumn 注解
2. SQL查询最终实现
文件: d:\EPproject\LabelReplaceServer\src\DAL\Repositories\LabelReplaceRepository.cs
新增CTE:
-
✅ CustomerLabelRates (步骤2)
- 计算每个客户的标签率:有标签订单数 / 总订单数
- 用途:在ArrivalRequests中判断是否应用固定考核时间
-
✅ DailyBeforeNoonPassed (步骤14.5) - 新增
- 统计16点前到仓且考核通过的包裹数量
- 条件:HOUR(ar.到货时间) < 16 AND 完成时间 <= 考核时间
-
✅ DailyAfternoonPassed (步骤14.6) - 新增
- 统计16点后到仓且考核通过的包裹数量
- 条件:HOUR(ar.到货时间) >= 16 AND 完成时间 <= 考核时间
修改的CTE:
-
✅ ArrivalRequests (步骤3)
- 新增字段:
客户标签率 - 重新实现
考核时间逻辑:- 标签率 >= 80%:到仓时间<16:00 → 次日16:00;≥16:00 → 次日23:59
- 标签率 < 80%:考核时间设为NULL,后续使用完成时间
- JOIN: 新增 LEFT JOIN CustomerLabelRates
- 新增字段:
-
✅ DailyBase (步骤9)
- 新增统计:
16点前到仓包裹数 - 新增统计:
16点后到仓包裹数
- 新增统计:
-
✅ Daily24HCompletedOrders (步骤14)
- 修改分组依据:from
oss.首次成功日期toar.到货日期 - 修改完成条件:新的二阶段判断逻辑
- 标签率≥80%:完成时间 <= 考核时间
- 标签率<80%:考核时间为NULL,直接算达标
- 修改分组依据:from
-
✅ DailyStatsWithPrev (步骤15)
- 新增字段传递:
16点前到仓包裹数,16点后到仓包裹数
- 新增字段传递:
最终SELECT修改:
-
✅ 新增输出列:
当天应该换单数: 通过变量计算 = 累计要换的总单数 + 当日新增换单数16点前到仓包裹数: 直接输出16点后到仓包裹数: 直接输出16点前考核通过包裹数: ✨ 新增 - 16点前到仓的通过考核包裹数16点后考核通过包裹数: ✨ 新增 - 16点后到仓的通过考核包裹数
-
✅ 修改24小时换单率计算:
- 分母:从
当日完成数改为当天应该换单数 - 分子:保持
24H内完成数 - 新公式:
24H内完成数 / 当天应该换单数 * 100
- 分母:从
-
✅ 移除系统标签率:
- 不再在SELECT中计算和输出系统标签率
- 精简了最终输出,提高查询性能
3. C#代码映射(最终版本)
文件: d:\EPproject\LabelReplaceServer\src\DAL\Repositories\LabelReplaceRepository.cs (L1084-1105)
映射调整:
// 保留
ShouldReplaceCount = reader["当天应该换单数"] != DBNull.Value ? Convert.ToInt32(reader["当天应该换单数"]) : 0,
BeforeNoonArrivedCount = reader["16点前到仓包裹数"] != DBNull.Value ? Convert.ToInt32(reader["16点前到仓包裹数"]) : 0,
AfternoonArrivedCount = reader["16点后到仓包裹数"] != DBNull.Value ? Convert.ToInt32(reader["16点后到仓包裹数"]) : 0,
// 新增
BeforeNoonPassedCount = reader["16点前考核通过包裹数"] != DBNull.Value ? Convert.ToInt32(reader["16点前考核通过包裹数"]) : 0,
AfternoonPassedCount = reader["16点后考核通过包裹数"] != DBNull.Value ? Convert.ToInt32(reader["16点后考核通过包裹数"]) : 0,
// 移除
// LabelRate = reader["系统标签率"] as string ?? "0.00%",
核心指标定义
16点前到仓 vs 16点前考核通过的区别
| 指标 | 定义 | SQL条件 | 用途 |
|---|---|---|---|
| 16点前到仓包裹数 | 当日16:00前到仓的所有包裹 | HOUR(到货时间) < 16 |
统计到仓分布 |
| 16点前考核通过包裹数 | 当日16:00前到仓且考核通过的包裹 | HOUR(到货时间) < 16 AND 完成时间 <= 考核时间 |
统计实际达成率 |
16点后到仓 vs 16点后考核通过的区别
| 指标 | 定义 | SQL条件 | 用途 |
|---|---|---|---|
| 16点后到仓包裹数 | 当日16:00后到仓的所有包裹 | HOUR(到货时间) >= 16 |
统计到仓分布 |
| 16点后考核通过包裹数 | 当日16:00后到仓且考核通过的包裹 | HOUR(到货时间) >= 16 AND 完成时间 <= 考核时间 |
统计实际达成率 |
考核时间逻辑(基于客户标签率)
标签率 >= 80%
- 到仓时间 < 16:00:考核时间 = 次日16:00
- 到仓时间 >= 16:00:考核时间 = 次日23:59
标签率 < 80%
- 考核时间 = 包裹实际换单完成时间(即完成就过关)
24小时换单完成率(已修改)
定义:通过24小时内完成考核的包裹数 / 当天应该换单数
计算逻辑:
24H完成率 = (
COUNT(DISTINCT
WHERE 完成时间 <= 考核时间
)
) / (累计要换的总单数 + 当日新增换单数) * 100%
说明:
- 分子:不受到仓时间影响,直接判断是否在考核时间内完成
- 分母:改为"当天应该完成的总包裹数"而不是"完成的包裹数"
编译检查结果
✅ DailyLabelStatsChineseDto.cs: 无新增诊断错误 ✅ LabelReplaceRepository.cs: 无新增诊断错误
SQL逻辑关键验证点
✅ 时区处理
- 所有到仓时间:使用
a.到货时间(已是UTC-5) - 完成时间比较:使用
CONVERT_TZ(oss.首次成功时间, '+00:00', '-05:00')转为UTC-5
✅ 标签率判断
- 清晰的二阶段逻辑:高标签率用固定时间,低标签率用完成时间
- 两个新增CTE独立处理16点前后的考核通过统计
✅ 16点分段统计
- 16点前:
HOUR(ar.到货时间) < 16 - 16点后:
HOUR(ar.到货时间) >= 16 - 两个条件互补,无重叠无遗漏
✅ 考核通过判断
- 标签率≥80%:
CONVERT_TZ(oss.首次成功时间, '+00:00', '-05:00') <= ar.考核时间 - 标签率<80%:
ar.考核时间 IS NULL时直接判定为达标 - 两个条件通过OR连接,完全覆盖所有情况
字段映射关系
输入(SQL列) → 输出(DTO属性)
| SQL列名 | DTO属性 | 数据类型 | 说明 |
|---|---|---|---|
| 日期 | Date | string | yyyy-MM-dd格式 |
| 当日新增换单数 | DailyNewReplaceCount | int | 当日新增 |
| 累计要换的总单数 | CumulativeTotalReplaceCount | int | 历史累计 |
| 当天应该换单数 | ShouldReplaceCount | int | 应该完成的总数 |
| 换单失败未完结订单 | UnfinishedFailureCount | int | 未完结 |
| 当日换单失败 | DailyFailureCount | int | 当日失败 |
| 当日换单成功数 | DailySuccessCount | int | 当日成功 |
| 当日STOP数 | DailyStopCount | int | STOP标签数 |
| 16点前到仓包裹数 | BeforeNoonArrivedCount | int | 到仓分布 |
| 16点后到仓包裹数 | AfternoonArrivedCount | int | 到仓分布 |
| 16点前考核通过包裹数 | BeforeNoonPassedCount | int | ✨ 新增 |
| 16点后考核通过包裹数 | AfternoonPassedCount | int | ✨ 新增 |
| 24H换单率 | Rate24Hour | string | "xx.xx%" |
| 当天换单完成率 | DailyCompletionRate | string | "xx.xx%" |
| 当日标签推送数 | DailyLabelPushCount | int | 推送数 |
| 当日扫描数 | DailyScanCount | int | 扫描数 |
| 数据拉取时间(UTC_5) | DataFetchTime | DateTime | 查询时间 |
性能影响分析
正面影响
✅ 性能优化:
- 移除了系统标签率的复杂子查询
- 减少了每行数据的计算复杂度
- 最终输出只包含必要的字段
新增操作
⚠️ 两个新的CTE:
- DailyBeforeNoonPassed(16点前考核通过)
- DailyAfternoonPassed(16点后考核通过)
- 影响:轻微,因为逻辑与Daily24HCompletedOrders类似
建议优化
- 添加索引:
CREATE INDEX idx_lrr_custom_label ON label_replace_requests(CustomerId, Label); - 添加索引:
CREATE INDEX idx_lsh_result_createdat ON label_scan_history(Result, CreatedAt);
实现完成度
✅ 100% 完成
- ✅ DTO类修改(移除LabelRate,新增两个16点考核通过字段)
- ✅ SQL查询重写(新增DailyBeforeNoonPassed/DailyAfternoonPassed,移除系统标签率)
- ✅ C#映射更新(移除LabelRate映射,新增两个16点考核映射)
- ✅ 编译无错误
- ✅ 所有新增需求指标已实现
- ✅ 代码符合现有风格