-- 每日标签统计综合查询(重新设计 - 基于到货交接单) -- 设计逻辑: -- 1. 要换多少单:根据 arrival_handover_forms 的 HandoverNumber 关联 label_replace_requests -- 2. 关联方式:BillOfLadingNumber 或 MasterPackageNumber 与 HandoverNumber 匹配 -- 3. 到货日期本身就是UTC-5,不需要转换 -- 4. 统计维度:日期、当日新增换单数、累计要换的总单数、换单失败未完结订单等 WITH -- 步骤1:获取所有到货交接单,日期已是UTC-5 ArrivalFormsWithDate AS ( SELECT a.Id, a.HandoverNumber, DATE(a.ReceiptTime) AS 到货日期, a.ReceiptTime AS 到货时间 FROM arrival_handover_forms a ), -- 步骤2:关联到货交接单与换单请求(只取Label有值的),并记录订单级别的信息,计算考核时间 ArrivalRequests AS ( SELECT a.到货日期, a.到货时间, l.Id AS RequestId, l.NeutralWaybillNumber, l.BillOfLadingNumber, l.MasterPackageNumber, l.Label, l.LabelRetrievedAt, l.CustomerId, -- 考核时间:标签推送时间与到仓时间比较,哪个最新用哪个 CASE WHEN l.LabelRetrievedAt IS NULL THEN a.到货时间 WHEN l.LabelRetrievedAt > a.到货时间 THEN l.LabelRetrievedAt ELSE a.到货时间 END AS 考核时间 FROM ArrivalFormsWithDate a INNER JOIN label_replace_requests l ON l.BillOfLadingNumber = a.HandoverNumber OR l.MasterPackageNumber = a.HandoverNumber WHERE l.Label IS NOT NULL AND l.Label != "" ), -- 步骤3:获取每个订单的扫描记录情况(按天) DailyScanStatus AS ( SELECT s.NeutralWaybillNumber, DATE(CONVERT_TZ(s.CreatedAt, '+00:00', '-05:00')) AS 日期, MAX(CASE WHEN s.Result = 0 THEN 1 ELSE 0 END) AS 当日是否成功, MAX(CASE WHEN s.Result != 0 THEN 1 ELSE 0 END) AS 当日是否失败 FROM label_scan_history s INNER JOIN label_replace_requests l ON s.NeutralWaybillNumber = l.NeutralWaybillNumber WHERE l.Label IS NOT NULL AND l.Label != "" GROUP BY s.NeutralWaybillNumber, DATE(CONVERT_TZ(s.CreatedAt, '+00:00', '-05:00')) ), -- 步骤4:获取每个订单是否曾经成功,以及首次成功日期和时间 OverallScanStatus AS ( SELECT s.NeutralWaybillNumber, MAX(CASE WHEN s.Result = 0 THEN 1 ELSE 0 END) AS 曾成功, MIN(CASE WHEN s.Result = 0 THEN DATE(CONVERT_TZ(s.CreatedAt, '+00:00', '-05:00')) ELSE NULL END) AS 首次成功日期, MIN(CASE WHEN s.Result = 0 THEN s.CreatedAt ELSE NULL END) AS 首次成功时间 FROM label_scan_history s GROUP BY s.NeutralWaybillNumber ), -- 步骤5:从数据中收集所有日期 AllDates AS ( SELECT 到货日期 AS 日期 FROM ArrivalRequests UNION SELECT 日期 FROM DailyScanStatus UNION SELECT DATE(CONVERT_TZ(l.LabelRetrievedAt, '+00:00', '-05:00')) AS 日期 FROM label_replace_requests l WHERE l.LabelRetrievedAt IS NOT NULL ), -- 步骤6:去重并排序日期 DistinctDates AS ( SELECT DISTINCT 日期 FROM AllDates ORDER BY 日期 ), -- 步骤7:获取最新日期 LatestDate AS ( SELECT MAX(日期) AS 日期 FROM DistinctDates ), -- 步骤8:每日基础统计 - 当日新增换单数 DailyBase AS ( SELECT dd.日期, -- 当日新增换单数:当天到货并且推送了标签数据的订单 COUNT(DISTINCT CASE WHEN ar.到货日期 = dd.日期 AND ar.LabelRetrievedAt IS NOT NULL THEN ar.RequestId END) AS 当日新增换单数, -- 当天标签推送数 COUNT(DISTINCT CASE WHEN DATE(CONVERT_TZ(ar.LabelRetrievedAt, '+00:00', '-05:00')) = dd.日期 THEN ar.RequestId END) AS 当日标签推送数 FROM DistinctDates dd CROSS JOIN ArrivalRequests ar GROUP BY dd.日期 ), -- 步骤9:每日扫描统计(扫描次数),换单成功数去重 DailyScanMetrics AS ( SELECT DATE(CONVERT_TZ(s.CreatedAt, '+00:00', '-05:00')) AS 日期, COUNT(*) AS 当日扫描数, -- 当日STOP数(同样去重) COUNT(DISTINCT CASE WHEN s.Result = 0 AND s.Description LIKE '%成功返回STOP标签%' THEN s.NeutralWaybillNumber END) AS 当日STOP数 FROM label_scan_history s INNER JOIN label_replace_requests l ON s.NeutralWaybillNumber = l.NeutralWaybillNumber WHERE l.Label IS NOT NULL AND l.Label != "" GROUP BY DATE(CONVERT_TZ(s.CreatedAt, '+00:00', '-05:00')) ), -- 步骤9b:每日换单成功数(去重) DailySuccessCount AS ( SELECT scan_date AS 日期, COUNT(DISTINCT NeutralWaybillNumber) AS 当日换单成功数 FROM ( -- 找出每个包裹每天最新的成功记录 SELECT s.NeutralWaybillNumber, DATE(CONVERT_TZ(s.CreatedAt, '+00:00', '-05:00')) AS scan_date, ROW_NUMBER() OVER (PARTITION BY s.NeutralWaybillNumber, DATE(CONVERT_TZ(s.CreatedAt, '+00:00', '-05:00')) ORDER BY s.CreatedAt DESC) AS rn FROM label_scan_history s INNER JOIN label_replace_requests l ON s.NeutralWaybillNumber = l.NeutralWaybillNumber WHERE l.Label IS NOT NULL AND l.Label != "" AND s.Result = 0 ) AS s WHERE rn = 1 GROUP BY scan_date ), -- 步骤10:历史日期的换单失败未完结统计(非最新日期) HistoryUnfinished AS ( SELECT dd.日期, COUNT(DISTINCT CASE -- 当天失败并且当天没有成功的订单 WHEN dss.日期 = dd.日期 AND dss.当日是否失败 = 1 AND dss.当日是否成功 = 0 THEN dss.NeutralWaybillNumber END) AS 换单失败未完结订单 FROM DistinctDates dd LEFT JOIN DailyScanStatus dss ON dd.日期 = dss.日期 CROSS JOIN LatestDate ld WHERE dd.日期 != ld.日期 GROUP BY dd.日期 ), -- 步骤11:最新日期的换单失败未完结统计(所有历史从未成功的) LatestUnfinished AS ( SELECT ld.日期, COUNT(DISTINCT CASE -- 必须同时满足: -- 1. 有过扫描记录(OverallScanStatus中有该订单 -- 2. 从未成功(曾成功 = 0) -- 3. 并且至少有一次失败记录 WHEN oss.曾成功 = 0 THEN ar.NeutralWaybillNumber END) AS 换单失败未完结订单 FROM LatestDate ld CROSS JOIN ArrivalRequests ar INNER JOIN OverallScanStatus oss ON ar.NeutralWaybillNumber = oss.NeutralWaybillNumber GROUP BY ld.日期 ), -- 步骤12:每日换单失败订单统计(当天失败并且当天没成功的订单数) DailyFailedOrders AS ( SELECT 日期, COUNT(DISTINCT CASE WHEN 当日是否失败 = 1 AND 当日是否成功 = 0 THEN NeutralWaybillNumber END) AS 当日换单失败 FROM DailyScanStatus GROUP BY 日期 ), -- 步骤13:每日完成订单统计 - 订单必须在我们关联的ArrivalRequests中 DailyCompletedOrders AS ( SELECT oss.首次成功日期 AS 日期, COUNT(DISTINCT oss.NeutralWaybillNumber) AS 当日完成数 FROM OverallScanStatus oss INNER JOIN ArrivalRequests ar ON oss.NeutralWaybillNumber = ar.NeutralWaybillNumber WHERE oss.曾成功 = 1 GROUP BY oss.首次成功日期 ), -- 步骤14:24小时换单完成订单统计 - 新逻辑:按到仓时间16点 cutoff判断 Daily24HCompletedOrders AS ( SELECT -- 统计到完成日期的下一天 DATE(CONVERT_TZ(oss.首次成功时间, '+00:00', '-05:00')) AS 日期, COUNT(DISTINCT ar.NeutralWaybillNumber) AS 24H内完成数 FROM ArrivalRequests ar INNER JOIN OverallScanStatus oss ON ar.NeutralWaybillNumber = oss.NeutralWaybillNumber WHERE oss.曾成功 = 1 AND oss.首次成功时间 IS NOT NULL AND ar.到货时间 IS NOT NULL AND ( -- 情况1:到仓时间在当日16点前,完成时间在次日16点前 (HOUR(ar.到货时间) < 16 AND CONVERT_TZ(oss.首次成功时间, '+00:00', '-05:00') <= DATE_ADD(ar.到货时间, INTERVAL 1 DAY) + INTERVAL 16 HOUR AND CONVERT_TZ(oss.首次成功时间, '+00:00', '-05:00') > ar.到货时间) OR -- 情况2:到仓时间在当日16点及以后,完成时间在次日23:59:59前 (HOUR(ar.到货时间) >= 16 AND CONVERT_TZ(oss.首次成功时间, '+00:00', '-05:00') <= DATE_ADD(DATE(ar.到货时间), INTERVAL 2 DAY) - INTERVAL 1 SECOND AND CONVERT_TZ(oss.首次成功时间, '+00:00', '-05:00') > ar.到货时间) ) GROUP BY DATE(CONVERT_TZ(oss.首次成功时间, '+00:00', '-05:00')) ), -- 步骤14a:当日换单成功统计(分子:到仓12点前且当天完成的包裹) DailySameDaySuccessOrders AS ( SELECT ar.到货日期 AS 日期, COUNT(DISTINCT ar.NeutralWaybillNumber) AS 当日12点前到且当日完成数 FROM ArrivalRequests ar INNER JOIN OverallScanStatus oss ON ar.NeutralWaybillNumber = oss.NeutralWaybillNumber WHERE oss.曾成功 = 1 AND ar.到货时间 IS NOT NULL AND oss.首次成功时间 IS NOT NULL -- 到仓时间在当日12点前 AND HOUR(ar.到货时间) < 12 -- 完成时间在当日23:59:59前 AND DATE(CONVERT_TZ(oss.首次成功时间, '+00:00', '-05:00')) = ar.到货日期 GROUP BY ar.到货日期 ), -- 步骤14b:每日总到仓包裹数(分母:当日23:59前到仓的总包裹数) DailyTotalArrivedOrders AS ( SELECT 到货日期 AS 日期, COUNT(DISTINCT RequestId) AS 当日总到仓包裹数 FROM ArrivalRequests GROUP BY 到货日期 ), -- 步骤15:完整的每日统计基础 - 准备每日的新增和完成,并获取前一日数据 DailyStatsWithPrev AS ( SELECT db.日期, db.当日新增换单数, db.当日标签推送数, COALESCE(dco.当日完成数, 0) AS 当日完成数, COALESCE(dc24h.24H内完成数, 0) AS 24H内完成数, COALESCE(dsds.当日12点前到且当日完成数, 0) AS 当日12点前到且当日完成数, COALESCE(dtao.当日总到仓包裹数, 0) AS 当日总到仓包裹数, -- 获取前一日的新增 LAG(db.当日新增换单数, 1, 0) OVER (ORDER BY db.日期) AS 前一日新增, -- 获取前一日的完成数 LAG(COALESCE(dco.当日完成数, 0), 1, 0) OVER (ORDER BY db.日期) AS 前一日完成数, -- 行号 ROW_NUMBER() OVER (ORDER BY db.日期) AS rn FROM DailyBase db LEFT JOIN DailyCompletedOrders dco ON db.日期 = dco.日期 LEFT JOIN Daily24HCompletedOrders dc24h ON db.日期 = dc24h.日期 LEFT JOIN DailySameDaySuccessOrders dsds ON db.日期 = dsds.日期 LEFT JOIN DailyTotalArrivedOrders dtao ON db.日期 = dtao.日期 ORDER BY db.日期 ) -- 步骤16:计算累计数据并最终输出 SELECT 日期, 当日新增换单数, 累计要换的总单数, 换单失败未完结订单, 当日换单失败, 当日换单成功数, 当日STOP数, -- 24小时换单成功率(新逻辑) CASE WHEN 当日完成数 = 0 THEN '0.00%' ELSE CONCAT(ROUND(24H内完成数 / 当日完成数 * 100, 2), '%') END AS 24小时换单成功率, -- 当日换单成功率(新逻辑) CASE WHEN 当日总到仓包裹数 = 0 THEN '0.00%' ELSE CONCAT(ROUND(当日12点前到且当日完成数 / 当日总到仓包裹数 * 100, 2), '%') END AS 当日换单成功率, 当日标签推送数, 当日扫描数, 数据拉取时间(UTC_5) FROM ( SELECT t.日期, t.当日新增换单数, t.当日12点前到且当日完成数, t.当日总到仓包裹数, -- 使用变量保持状态,每次计算前一天的累计 -- 公式:累计 = MAX(0, 前一日累计 + 前一日新增 - 前一日完成) -- 第1天直接用当日新增 @running_total := GREATEST(0, CASE WHEN t.rn = 1 THEN t.当日新增换单数 ELSE @running_total + t.前一日新增 - t.前一日完成数 END ) AS 累计要换的总单数, -- 根据是否是最新日期选择不同的未完结统计 COALESCE( CASE WHEN t.日期 = (SELECT 日期 FROM LatestDate) THEN lu.换单失败未完结订单 ELSE hu.换单失败未完结订单 END, 0 ) AS 换单失败未完结订单, COALESCE(dfo.当日换单失败, 0) AS 当日换单失败, COALESCE(dsc.当日换单成功数, 0) AS 当日换单成功数, COALESCE(dsm.当日STOP数, 0) AS 当日STOP数, t.24H内完成数, t.当日完成数, t.当日标签推送数, COALESCE(dsm.当日扫描数, 0) AS 当日扫描数, (UTC_TIMESTAMP() - INTERVAL 5 HOUR) AS 数据拉取时间(UTC_5) FROM DailyStatsWithPrev t LEFT JOIN DailyScanMetrics dsm ON t.日期 = dsm.日期 LEFT JOIN DailySuccessCount dsc ON t.日期 = dsc.日期 LEFT JOIN HistoryUnfinished hu ON t.日期 = hu.日期 LEFT JOIN LatestUnfinished lu ON t.日期 = lu.日期 LEFT JOIN DailyFailedOrders dfo ON t.日期 = dfo.日期 -- 初始化变量 CROSS JOIN (SELECT @running_total := 0) AS init -- 数据验证:排除无效日期 WHERE t.日期 IS NOT NULL ORDER BY t.日期 ) AS subquery -- 数据验证:排除扫描数为0的日期(如果需要) -- WHERE 当日扫描数 > 0 ORDER BY 日期 DESC;