Files
LabelChange-server/最新运营监控_优化版.sql
2026-06-01 16:30:29 +08:00

347 lines
15 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. 到货时间ReceiptTime本身是UTC-5不需要转换
-- 2. 其他时间标签推送时间、扫描时间是UTC-0需要转换为UTC-5
-- 3. 标签率计算:首次扫描前推送的标签数/交接单总订单数,无扫描记录则用当前标签数/总订单数
-- 4. 考核时间仅针对标签率≥80%的有标签订单计算
WITH
-- 步骤1获取所有到货交接单时间已是UTC-5
ArrivalForms AS (
SELECT
Id AS FormId,
HandoverNumber,
ReceiptTime,
DATE(ReceiptTime) AS ReceiptDate,
HOUR(ReceiptTime) AS ReceiptHour
FROM arrival_handover_forms
),
-- 步骤2获取所有换单请求
LabelRequests AS (
SELECT
Id AS RequestId,
NeutralWaybillNumber,
BillOfLadingNumber,
MasterPackageNumber,
Label,
LabelRetrievedAt,
CreatedAt AS RequestCreatedAt,
-- 转换为UTC-5时间
CASE WHEN LabelRetrievedAt IS NOT NULL THEN CONVERT_TZ(LabelRetrievedAt, '+00:00', '-05:00') END AS LabelRetrievedAt_UTC5,
DATE(CONVERT_TZ(LabelRetrievedAt, '+00:00', '-05:00')) AS LabelRetrievedDate_UTC5
FROM label_replace_requests
),
-- 步骤3关联到货交接单和换单请求优先匹配大箱号无大箱号匹配则用提单号
FormRequestRelation AS (
SELECT
-- 优先取大箱号匹配的交接单信息,无则取提单号匹配的
COALESCE(f_m.FormId, f_b.FormId) AS FormId,
COALESCE(f_m.HandoverNumber, f_b.HandoverNumber) AS HandoverNumber,
COALESCE(f_m.ReceiptTime, f_b.ReceiptTime) AS ReceiptTime,
COALESCE(f_m.ReceiptDate, f_b.ReceiptDate) AS ReceiptDate,
COALESCE(f_m.ReceiptHour, f_b.ReceiptHour) AS ReceiptHour,
r.RequestId,
r.NeutralWaybillNumber,
r.Label,
r.LabelRetrievedAt,
r.LabelRetrievedAt_UTC5,
r.LabelRetrievedDate_UTC5,
r.RequestCreatedAt,
-- 是否有标签
CASE WHEN r.Label IS NOT NULL AND r.Label != '' THEN 1 ELSE 0 END AS HasLabel
FROM LabelRequests r
-- 先匹配大箱号
LEFT JOIN ArrivalForms f_m ON r.MasterPackageNumber = f_m.HandoverNumber
-- 大箱号匹配不到再匹配提单号
LEFT JOIN ArrivalForms f_b ON r.BillOfLadingNumber = f_b.HandoverNumber
-- 只保留有匹配到交接单的订单
WHERE COALESCE(f_m.FormId, f_b.FormId) IS NOT NULL
),
-- 步骤4获取每个交接单的首次扫描时间
FormFirstScan AS (
SELECT
fr.FormId,
MIN(s.CreatedAt) AS FirstScanTime,
CONVERT_TZ(MIN(s.CreatedAt), '+00:00', '-05:00') AS FirstScanTime_UTC5
FROM FormRequestRelation fr
LEFT JOIN label_scan_history s ON fr.NeutralWaybillNumber = s.NeutralWaybillNumber
GROUP BY fr.FormId
),
-- 步骤5先计算每个交接单的总订单数和达标阈值
FormTotalOrderCount AS (
SELECT
FormId,
COUNT(DISTINCT RequestId) AS TotalOrderCount,
CEIL(COUNT(DISTINCT RequestId) * 0.8) AS QualifyNeedCount
FROM FormRequestRelation
GROUP BY FormId
),
-- 步骤6计算每个交接单每个标签的推送时间及排序匹配达标阈值
FormLabelPushTimes AS (
SELECT
fr.FormId,
fr.LabelRetrievedAt_UTC5,
-- 按推送时间排序,计算累计推送的标签数
ROW_NUMBER() OVER (PARTITION BY fr.FormId ORDER BY fr.LabelRetrievedAt_UTC5) AS PushOrder,
ftoc.QualifyNeedCount
FROM FormRequestRelation fr
INNER JOIN FormTotalOrderCount ftoc ON fr.FormId = ftoc.FormId
WHERE fr.HasLabel = 1 AND fr.LabelRetrievedAt_UTC5 IS NOT NULL
),
-- 步骤7计算每个交接单的首次达标日期和时间
FormFirstQualifiedDate AS (
SELECT
FormId,
MIN(DATE(LabelRetrievedAt_UTC5)) AS FirstQualifiedDate,
MIN(LabelRetrievedAt_UTC5) AS FirstQualifiedTime_UTC5
FROM FormLabelPushTimes
WHERE PushOrder >= QualifyNeedCount
GROUP BY FormId
),
-- 步骤8计算每个交接单的标签率按当前实际情况统计无需冻结
FormLabelRateAndQualifiedDate AS (
SELECT
fr.FormId,
ftoc.TotalOrderCount,
-- 有标签的订单数
COUNT(DISTINCT CASE WHEN fr.HasLabel = 1 THEN fr.RequestId END) AS HasLabelOrderCount,
-- 标签率:当前有标签订单数 / 总订单数
CASE
WHEN ftoc.TotalOrderCount = 0 THEN 0
ELSE COUNT(DISTINCT CASE WHEN fr.HasLabel = 1 THEN fr.RequestId END) / ftoc.TotalOrderCount
END AS LabelRate,
fqd.FirstQualifiedDate,
fqd.FirstQualifiedTime_UTC5
FROM FormRequestRelation fr
INNER JOIN FormTotalOrderCount ftoc ON fr.FormId = ftoc.FormId
LEFT JOIN FormFirstQualifiedDate fqd ON fr.FormId = fqd.FormId
GROUP BY fr.FormId, ftoc.TotalOrderCount, fqd.FirstQualifiedDate, fqd.FirstQualifiedTime_UTC5
),
-- 步骤9计算每个订单的考核时间基于达标时间和到货时间较晚者
OrderAssessment AS (
SELECT
fr.*,
flr.LabelRate,
flr.FirstQualifiedDate,
flr.FirstQualifiedTime_UTC5,
fs.FirstScanTime_UTC5,
-- 订单创建日期UTC-5
DATE(CONVERT_TZ(fr.RequestCreatedAt, '+00:00', '-05:00')) AS RequestCreatedDate_UTC5,
-- 考核基准时间:如果首次扫描早于到仓则用首次扫描时间,再和标签率达标时间取较晚者
GREATEST(
CASE
WHEN fs.FirstScanTime_UTC5 IS NOT NULL AND fs.FirstScanTime_UTC5 < fr.ReceiptTime
THEN fs.FirstScanTime_UTC5
ELSE fr.ReceiptTime
END,
flr.FirstQualifiedTime_UTC5
) AS AssessmentBaseTime,
-- 考核时间计算仅标签率≥80%且有标签的订单)
CASE
WHEN flr.FirstQualifiedTime_UTC5 IS NOT NULL AND fr.HasLabel = 1 THEN
CASE
-- 考核基准时间16点前考核截止次日16点
WHEN HOUR(GREATEST(
CASE
WHEN fs.FirstScanTime_UTC5 IS NOT NULL AND fs.FirstScanTime_UTC5 < fr.ReceiptTime
THEN fs.FirstScanTime_UTC5
ELSE fr.ReceiptTime
END,
flr.FirstQualifiedTime_UTC5
)) < 16 THEN
DATE_ADD(
DATE_ADD(DATE(GREATEST(
CASE
WHEN fs.FirstScanTime_UTC5 IS NOT NULL AND fs.FirstScanTime_UTC5 < fr.ReceiptTime
THEN fs.FirstScanTime_UTC5
ELSE fr.ReceiptTime
END,
flr.FirstQualifiedTime_UTC5
)), INTERVAL 1 DAY),
INTERVAL 16 HOUR
)
-- 考核基准时间16点后考核截止次日23:59:59
ELSE
DATE_ADD(
DATE_ADD(DATE(GREATEST(
CASE
WHEN fs.FirstScanTime_UTC5 IS NOT NULL AND fs.FirstScanTime_UTC5 < fr.ReceiptTime
THEN fs.FirstScanTime_UTC5
ELSE fr.ReceiptTime
END,
flr.FirstQualifiedTime_UTC5
)), INTERVAL 1 DAY),
INTERVAL '23:59:59' HOUR_SECOND
)
END
ELSE NULL
END AS AssessmentTime
FROM FormRequestRelation fr
LEFT JOIN FormLabelRateAndQualifiedDate flr ON fr.FormId = flr.FormId
LEFT JOIN FormFirstScan fs ON fr.FormId = fs.FormId
WHERE fr.HasLabel = 1 -- 仅统计有标签的订单
),
-- 步骤7获取每个订单的扫描状态
OrderScanStatus AS (
SELECT
s.NeutralWaybillNumber,
-- 首次成功时间UTC-5
MIN(CASE WHEN s.Result = 0 THEN CONVERT_TZ(s.CreatedAt, '+00:00', '-05:00') END) AS FirstSuccessTime_UTC5,
DATE(MIN(CASE WHEN s.Result = 0 THEN CONVERT_TZ(s.CreatedAt, '+00:00', '-05:00') END)) AS FirstSuccessDate_UTC5,
-- 是否成功
MAX(CASE WHEN s.Result = 0 THEN 1 ELSE 0 END) AS IsSuccess,
-- 是否是STOP标签
MAX(CASE WHEN s.Result = 0 AND s.Description LIKE '%成功返回STOP标签%' THEN 1 ELSE 0 END) AS IsStopLabel
FROM label_scan_history s
GROUP BY s.NeutralWaybillNumber
),
-- 步骤8合并订单信息和扫描状态
OrderFullInfo AS (
SELECT
oa.*,
oss.FirstSuccessTime_UTC5,
oss.FirstSuccessDate_UTC5,
oss.IsSuccess,
oss.IsStopLabel,
-- 是否在考核时间内完成
CASE
WHEN oa.AssessmentTime IS NOT NULL AND oss.IsSuccess = 1
AND oss.FirstSuccessTime_UTC5 <= oa.AssessmentTime
THEN 1 ELSE 0
END AS IsCompletedInAssessment
FROM OrderAssessment oa
LEFT JOIN OrderScanStatus oss ON oa.NeutralWaybillNumber = oss.NeutralWaybillNumber
),
-- 步骤9独立统计每日扫描次数不关联其他表统计所有扫描记录
DailyScanCount AS (
SELECT
DATE(CONVERT_TZ(CreatedAt, '+00:00', '-05:00')) AS ,
COUNT(*) AS
FROM label_scan_history
GROUP BY DATE(CONVERT_TZ(CreatedAt, '+00:00', '-05:00'))
),
-- 步骤10独立统计每日成功换单的去重订单数不关联其他表排除STOP标签
DailySuccessCount AS (
SELECT
DATE(CONVERT_TZ(CreatedAt, '+00:00', '-05:00')) AS ,
COUNT(DISTINCT NeutralWaybillNumber) AS
FROM label_scan_history
WHERE Result = 0
AND Description NOT LIKE '%成功返回STOP标签%' -- 排除STOP标签
GROUP BY DATE(CONVERT_TZ(CreatedAt, '+00:00', '-05:00'))
),
-- 步骤11独立统计每日失败换单的去重订单数当日有失败且无成功不限制是否有标签
DailyFailCount AS (
SELECT
,
COUNT(DISTINCT NeutralWaybillNumber) AS
FROM (
SELECT
DATE(CONVERT_TZ(CreatedAt, '+00:00', '-05:00')) AS ,
NeutralWaybillNumber,
MAX(CASE WHEN Result = 0 THEN 1 ELSE 0 END) AS has_success,
MAX(CASE WHEN Result != 0 THEN 1 ELSE 0 END) AS has_fail
FROM label_scan_history
GROUP BY DATE(CONVERT_TZ(CreatedAt, '+00:00', '-05:00')), NeutralWaybillNumber
) t
WHERE has_fail = 1 AND has_success = 0
GROUP BY
),
-- 步骤12独立统计每日STOP标签的去重订单数
DailyStopCount AS (
SELECT
DATE(CONVERT_TZ(CreatedAt, '+00:00', '-05:00')) AS ,
COUNT(DISTINCT NeutralWaybillNumber) AS STOP数
FROM label_scan_history
WHERE Result = 0 AND Description LIKE '%成功返回STOP标签%'
GROUP BY DATE(CONVERT_TZ(CreatedAt, '+00:00', '-05:00'))
),
-- 步骤10获取所有日期维度
AllDates AS (
SELECT ReceiptDate AS FROM OrderFullInfo -- 到仓日期UTC-5
UNION
SELECT LabelRetrievedDate_UTC5 AS FROM OrderFullInfo WHERE LabelRetrievedDate_UTC5 IS NOT NULL -- 标签推送日期UTC-5
UNION
SELECT FirstSuccessDate_UTC5 AS FROM OrderFullInfo WHERE FirstSuccessDate_UTC5 IS NOT NULL -- 换单成功日期UTC-5
UNION
SELECT DATE(AssessmentBaseTime) AS FROM OrderFullInfo WHERE AssessmentBaseTime IS NOT NULL -- 考核基准日期UTC-5
UNION
SELECT DATE(AssessmentTime) AS FROM OrderFullInfo WHERE AssessmentTime IS NOT NULL -- 考核截止日期UTC-5
UNION
SELECT DATE(CONVERT_TZ(CreatedAt, '+00:00', '-05:00')) AS FROM label_scan_history WHERE CreatedAt IS NOT NULL -- 扫描日期转UTC-5
UNION
SELECT DATE(CONVERT_TZ(LabelRetrievedAt, '+00:00', '-05:00')) AS FROM label_replace_requests WHERE LabelRetrievedAt IS NOT NULL -- 标签推送日期转UTC-5
UNION
SELECT DATE(CONVERT_TZ(CreatedAt, '+00:00', '-05:00')) AS FROM label_scan_history WHERE Result = 0 AND CreatedAt IS NOT NULL -- 换单完成日期转UTC-5
UNION
SELECT FROM DailyScanCount
),
-- 步骤10去重排序日期
DistinctDates AS (
SELECT DISTINCT
FROM AllDates
ORDER BY
),
-- 步骤11每日指标汇总
DailyMetrics AS (
SELECT
dd.,
-- 当天新增换单数:到仓时间是当天的有标签订单数
COUNT(DISTINCT CASE WHEN ofi.ReceiptDate = dd. THEN ofi.RequestId END) AS ,
-- 当天应该换单数到仓时间是当天、标签率≥80%的订单(完全和考核时间无关)
COUNT(DISTINCT CASE
WHEN ofi.ReceiptDate = dd.
AND ofi.LabelRate >= 0.8
THEN ofi.RequestId
END) AS ,
-- 当日标签推送数:标签推送时间是当天的订单数
COUNT(DISTINCT CASE WHEN ofi.LabelRetrievedDate_UTC5 = dd. THEN ofi.RequestId END) AS ,
-- 累计要换的总单数:有标签、未完成换单、且有交接单号的订单总数
COUNT(DISTINCT CASE
WHEN ofi.HasLabel = 1
AND ofi.IsSuccess = 0
AND ofi.FormId IS NOT NULL -- 有交接单号
THEN ofi.RequestId
END) AS ,
-- 24小时换单成功数完成考核、且考核时间截止时间是当天的订单数
COUNT(DISTINCT CASE
WHEN ofi.AssessmentTime IS NOT NULL
AND DATE(ofi.AssessmentTime) = dd. -- 仅考核截止日期是当天
AND ofi.IsCompletedInAssessment = 1
AND ofi.FirstSuccessDate_UTC5 = dd.
THEN ofi.RequestId
END) AS 24H完成数
FROM DistinctDates dd
CROSS JOIN OrderFullInfo ofi
GROUP BY dd.
)
-- 最终输出
SELECT
dm.,
dm.,
dm.,
dm.,
COALESCE(dsucc., 0) AS ,
COALESCE(dfail., 0) AS ,
COALESCE(dstop.STOP数, 0) AS STOP数,
dm.24H完成数 AS 24,
dm.,
COALESCE(dsc., 0) AS ,
-- 当天换单完成率:当日换单完成数 / 当天应该换单数
CASE
WHEN dm. = 0 THEN '0.00%'
ELSE CONCAT(ROUND(COALESCE(dsucc., 0) / dm. * 100, 2), '%')
END AS ,
-- 24小时换单率考核时间内完成数 / 当天应该考核的订单总数
CASE
WHEN dm. = 0 THEN '0.00%'
ELSE CONCAT(ROUND(dm.24H完成数 / dm. * 100, 2), '%')
END AS 24,
-- 数据拉取时间
(UTC_TIMESTAMP() - INTERVAL 5 HOUR) AS UTC_5
FROM DailyMetrics dm
LEFT JOIN DailyScanCount dsc ON dm. = dsc.
LEFT JOIN DailySuccessCount dsucc ON dm. = dsucc.
LEFT JOIN DailyFailCount dfail ON dm. = dfail.
LEFT JOIN DailyStopCount dstop ON dm. = dstop.
ORDER BY dm. DESC;