237 lines
8.9 KiB
Markdown
237 lines
8.9 KiB
Markdown
# 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点考核映射)
|
||
- ✅ 编译无错误
|
||
- ✅ 所有新增需求指标已实现
|
||
- ✅ 代码符合现有风格
|
||
|