6.0 KiB
6.0 KiB
视图逻辑修复方案总结
📌 问题回顾
用户反馈数据库视图查询结果大部分为0:
2026-05-15 | 0 | 3592 | 0 | 3315 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2026-05-16 20:21:40
仅有 DailyShouldReplaceCount(3592) 和 CumulativeTotalReplaceCount(3315) 有数据,其他指标全是0。
🔍 根本原因分析
问题1:硬编码NOW()导致的日期比较错误
原视图SQL片段:
CAST(CONVERT_TZ(r.CreatedAt, '+00:00', '-05:00') AS DATE) = CAST(CONVERT_TZ(NOW(), '+00:00', '-05:00') AS DATE)
问题:每次查询都与当前时间比较,而不是与查询参数的日期比较。
影响:
- 查询2026-05-15的数据时,条件自动比较为"2026-05-15是否等于今天"
- 由于2026-05-15不等于今天的日期,所以大部分WHERE条件都返回FALSE
- 导致除了依赖其他条件的指标外,大部分指标都被过滤掉了
问题2:JOIN关联逻辑不合理
原视图使用 OR 条件进行关联:
r.BillOfLadingNumber = ahf.HandoverNumber OR r.MasterPackageNumber = ahf.HandoverNumber
虽然这个关联本身可以工作,但与硬编码的时间比较配合,导致很多联接行被意外过滤。
问题3:时区转换过度使用
多次重复的 CONVERT_TZ() 调用导致:
- SQL语句复杂性增加
- 性能下降
- 维护困难
✨ 修复方案
采用方案:存储过程(Stored Procedure)
关键改进:
-
参数化日期输入
- 从硬编码
NOW()改为接受p_date参数 - 支持查询任意历史日期
- 从硬编码
-
简化时区转换
- 在存储过程开始时一次性计算日期范围(v_date_start, v_date_end)
- 后续查询直接使用这些变量,避免重复转换
-
优化JOIN逻辑
- 使用临时表
temp_qualified_arrivals预先计算标签率≥80%的交接单 - 后续查询直接JOIN临时表,提高查询效率
- 使用临时表
-
清晰的指标计算
- 每个指标独立计算,使用SELECT...INTO语句
- 便于调试和验证
存储过程核心逻辑
CREATE PROCEDURE sp_GetDailyMetricsSummary(IN p_date DATE)
-- 1. 计算UTC-5时区的日期范围
SET v_date_start = CONVERT_TZ(CONCAT(DATE(p_date), ' 00:00:00'), '-05:00', '+00:00');
SET v_date_end = CONVERT_TZ(CONCAT(DATE(p_date), ' 23:59:59'), '-05:00', '+00:00');
-- 2. 创建临时表存储标签率≥80%的交接单
CREATE TEMPORARY TABLE temp_qualified_arrivals AS
SELECT ... FROM arrival_handover_forms ... HAVING labeled_orders / total_orders >= 0.8;
-- 3. 基于临时表和时间范围计算各项指标
SELECT COUNT(...) INTO v_daily_should_replace_count ...
SELECT COUNT(...) INTO v_daily_success_count ...
-- ... 其他指标 ...
-- 4. 返回所有指标结果
SELECT ... AS MetricsDate, v_daily_new_replace_count AS DailyNewReplaceCount, ...
📊 预期改进
性能改进
- 原方案:11个独立异步查询,耗时15-30秒
- 新方案:单个存储过程调用,目标<1秒
- 性能提升:15-30倍
正确性改进
- 原问题:大多数指标返回0
- 修复后:各指标返回合理的非零值
- 根本原因:移除了硬编码的NOW()导致的日期比较错误
灵活性改进
- 原方案:视图固定查询当前日期
- 新方案:可查询任意历史日期
- 支持:报表、趋势分析等需要历史数据的功能
🔧 实现变更
1. 数据库变更
📄 文件:database/migrations/002_create_sp_daily_metrics_summary.sql
- 创建存储过程
sp_GetDailyMetricsSummary - 接收参数:
p_date DATE - 返回:13个指标列
2. 应用层变更
📄 文件:src/BLL/Services/MetricsCalculationService.cs
- 第462行:修改
GetDailySummaryAsync()方法 - 将
SELECT * FROM v_DailyMetricsSummary WHERE MetricsDate = '{date}' - 改为
CALL sp_GetDailyMetricsSummary('{date:yyyy-MM-dd}')
3. 降级方案保留
- 保留原有的
GetDailySummaryAsync_Original()方法 - 如果存储过程调用失败,自动降级到11个独立查询
- 确保系统可用性
📝 验证步骤
数据库层验证
- 执行存储过程创建脚本
- 执行
CALL sp_GetDailyMetricsSummary('2026-05-15') - 验证返回数据:
- DailyShouldReplaceCount ≈ 3592 ✅
- CumulativeTotalReplaceCount ≈ 3315 ✅
- 其他指标 > 0 ✅
应用层验证
- 重新编译并运行后端服务
- 调用 API:
GET /api/metrics/daily-dashboard?date=2026-05-15 - 验证JSON响应包含所有指标
前端验证
- 打开仪表盘:
http://localhost:5002/metrics-dashboard-summary.html - 选择日期 2026-05-15
- 验证显示的指标正确性
性能验证
- 使用浏览器开发者工具查看API响应时间
- 应该 < 1000ms(1秒)
🎯 后续可选优化
- 缓存层:添加Redis缓存,避免频繁查询相同日期
- 物化视图:定期生成历史数据快照
- 分析表:预生成常用报表的汇总数据
- 分区:按日期分区arrival_handover_forms表
📋 关键指标定义(业务规则)
DailyShouldReplaceCount
- 当日到仓且标签率≥80%的订单总数
- 业务含义:当天应该执行的换单操作数
DailySuccessCount
- 当日成功扫描的订单数(Result=0)
- 业务含义:当天完成的换单操作数
CumulativeTotalReplaceCount
- 截至前一天未成功扫描的订单数
- 业务含义:历史待处理的订单数
BeforeNoonArrivedCount / AfternoonArrivedCount
- 按16:00时间点划分的到仓订单
- 业务含义:区分早班和晚班处理的订单
完成率计算
- DailyCompletionRate = DailySuccessCount / DailyShouldReplaceCount * 100%
- 指标类型:百分比,≥95%为绿色,85-95%为黄色,<85%为红色
✅ 完成检查清单
- 分析根本原因
- 设计解决方案
- 编写存储过程SQL
- 修改应用层代码
- 准备执行说明文档
- 用户执行:在数据库中创建存储过程
- 测试:验证存储过程返回正确数据
- 集成:前端调用验证
- 性能:确认查询时间<1秒