Files
LabelChange-server/daily-label-stats-chinese.sql
2026-06-01 16:30:29 +08:00

345 lines
14 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- 每日标签统计综合查询(重新设计 - 基于到货交接单)
-- 设计逻辑:
-- 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.
),
-- 步骤1424小时换单完成订单统计 - 新逻辑按到仓时间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;