# 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: 1. ✅ **CustomerLabelRates** (步骤2) - 计算每个客户的标签率:有标签订单数 / 总订单数 - 用途:在ArrivalRequests中判断是否应用固定考核时间 2. ✅ **DailyBeforeNoonPassed** (步骤14.5) - **新增** - 统计16点前到仓且**考核通过**的包裹数量 - 条件:HOUR(ar.到货时间) < 16 AND 完成时间 <= 考核时间 3. ✅ **DailyAfternoonPassed** (步骤14.6) - **新增** - 统计16点后到仓且**考核通过**的包裹数量 - 条件:HOUR(ar.到货时间) >= 16 AND 完成时间 <= 考核时间 ##### 修改的CTE: 1. ✅ **ArrivalRequests** (步骤3) - 新增字段:`客户标签率` - 重新实现`考核时间`逻辑: - 标签率 >= 80%:到仓时间<16:00 → 次日16:00;≥16:00 → 次日23:59 - 标签率 < 80%:考核时间设为NULL,后续使用完成时间 - JOIN: 新增 LEFT JOIN CustomerLabelRates 2. ✅ **DailyBase** (步骤9) - 新增统计:`16点前到仓包裹数` - 新增统计:`16点后到仓包裹数` 3. ✅ **Daily24HCompletedOrders** (步骤14) - 修改分组依据:from `oss.首次成功日期` to `ar.到货日期` - 修改完成条件:新的二阶段判断逻辑 - 标签率≥80%:完成时间 <= 考核时间 - 标签率<80%:考核时间为NULL,直接算达标 4. ✅ **DailyStatsWithPrev** (步骤15) - 新增字段传递:`16点前到仓包裹数`, `16点后到仓包裹数` ##### 最终SELECT修改: 1. ✅ **新增输出列**: - `当天应该换单数`: 通过变量计算 = 累计要换的总单数 + 当日新增换单数 - `16点前到仓包裹数`: 直接输出 - `16点后到仓包裹数`: 直接输出 - **`16点前考核通过包裹数`**: ✨ 新增 - 16点前到仓的通过考核包裹数 - **`16点后考核通过包裹数`**: ✨ 新增 - 16点后到仓的通过考核包裹数 2. ✅ **修改24小时换单率计算**: - 分母:从 `当日完成数` 改为 `当天应该换单数` - 分子:保持 `24H内完成数` - 新公式: `24H内完成数 / 当天应该换单数 * 100` 3. ✅ **移除系统标签率**: - 不再在SELECT中计算和输出系统标签率 - 精简了最终输出,提高查询性能 --- #### 3. C#代码映射(最终版本) **文件**: `d:\EPproject\LabelReplaceServer\src\DAL\Repositories\LabelReplaceRepository.cs` (L1084-1105) **映射调整**: ```csharp // 保留 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点考核映射) - ✅ 编译无错误 - ✅ 所有新增需求指标已实现 - ✅ 代码符合现有风格