Files
LabelChange-server/客户维度运营监控.sql
2026-06-01 16:30:29 +08:00

361 lines
17 KiB
SQL
Raw Permalink 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.

-- 客户维度每日标签统计综合查询
-- 设计逻辑和原运营监控SQL逻辑完全一致新增按CustomerCode维度分组统计
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获取所有换单请求关联客户表获取CustomerCode
LabelRequests AS (
SELECT
r.Id AS RequestId,
r.NeutralWaybillNumber,
r.BillOfLadingNumber,
r.MasterPackageNumber,
r.Label,
r.LabelRetrievedAt,
r.CreatedAt AS RequestCreatedAt,
c.CustomerCode, -- 通过customerid关联客户表获取客户代码
-- 转换为UTC-5时间
CASE WHEN r.LabelRetrievedAt IS NOT NULL THEN CONVERT_TZ(r.LabelRetrievedAt, '+00:00', '-05:00') END AS LabelRetrievedAt_UTC5,
DATE(CONVERT_TZ(r.LabelRetrievedAt, '+00:00', '-05:00')) AS LabelRetrievedDate_UTC5
FROM label_replace_requests r
LEFT JOIN customers c ON r.customerid = c.Id -- 通过customerid关联客户表
),
-- 步骤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,
r.CustomerCode, -- 传递客户代码
-- 是否有标签
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独立统计每日+客户维度成功换单的去重订单数
CustomerDailySuccessCount AS (
SELECT
DATE(CONVERT_TZ(s.CreatedAt, '+00:00', '-05:00')) AS ,
c.CustomerCode,
COUNT(DISTINCT s.NeutralWaybillNumber) AS
FROM label_scan_history s
INNER JOIN label_replace_requests r ON s.NeutralWaybillNumber = r.NeutralWaybillNumber
INNER JOIN customers c ON r.customerid = c.Id
WHERE s.Result = 0
GROUP BY DATE(CONVERT_TZ(s.CreatedAt, '+00:00', '-05:00')), c.CustomerCode
),
-- 步骤10独立统计每日+客户维度失败换单的去重订单数(当日有失败且无成功)
CustomerDailyFailCount AS (
SELECT
,
CustomerCode,
COUNT(DISTINCT NeutralWaybillNumber) AS
FROM (
SELECT
DATE(CONVERT_TZ(s.CreatedAt, '+00:00', '-05:00')) AS ,
c.CustomerCode,
s.NeutralWaybillNumber,
MAX(CASE WHEN s.Result = 0 THEN 1 ELSE 0 END) AS has_success,
MAX(CASE WHEN s.Result != 0 THEN 1 ELSE 0 END) AS has_fail
FROM label_scan_history s
INNER JOIN label_replace_requests r ON s.NeutralWaybillNumber = r.NeutralWaybillNumber
INNER JOIN customers c ON r.customerid = c.Id
GROUP BY DATE(CONVERT_TZ(s.CreatedAt, '+00:00', '-05:00')), c.CustomerCode, s.NeutralWaybillNumber
) t
WHERE has_fail = 1 AND has_success = 0
GROUP BY , CustomerCode
),
-- 步骤11独立统计每日+客户维度的扫描次数
CustomerDailyScanCount AS (
SELECT
DATE(CONVERT_TZ(s.CreatedAt, '+00:00', '-05:00')) AS ,
c.CustomerCode,
COUNT(*) AS
FROM label_scan_history s
INNER JOIN label_replace_requests r ON s.NeutralWaybillNumber = r.NeutralWaybillNumber
INNER JOIN customers c ON r.customerid = c.Id
GROUP BY DATE(CONVERT_TZ(s.CreatedAt, '+00:00', '-05:00')), c.CustomerCode
),
-- 步骤12独立统计每日+客户维度STOP标签的去重订单数
CustomerDailyStopCount AS (
SELECT
DATE(CONVERT_TZ(s.CreatedAt, '+00:00', '-05:00')) AS ,
c.CustomerCode,
COUNT(DISTINCT s.NeutralWaybillNumber) AS STOP数
FROM label_scan_history s
INNER JOIN label_replace_requests r ON s.NeutralWaybillNumber = r.NeutralWaybillNumber
INNER JOIN customers c ON r.customerid = c.Id
WHERE s.Result = 0 AND s.Description LIKE '%成功返回STOP标签%'
GROUP BY DATE(CONVERT_TZ(s.CreatedAt, '+00:00', '-05:00')), c.CustomerCode
),
-- 步骤12获取所有日期维度
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
),
-- 步骤10去重排序日期
DistinctDates AS (
SELECT DISTINCT
FROM AllDates
ORDER BY
),
-- 步骤13每日+客户维度指标汇总
DailyCustomerMetrics AS (
SELECT
dd.,
ofi.CustomerCode, -- 客户代码维度
-- 当天新增换单数:到仓时间是当天的有标签订单数
COUNT(DISTINCT CASE WHEN ofi.ReceiptDate = dd. THEN ofi.RequestId END) AS ,
-- 当天应该换单数有考核时间、考核基准日期是当天或考核截止日期是当天且换单完成时间小于等于统计日期的订单有考核时间默认标签率≥80%
COUNT(DISTINCT CASE
WHEN ofi.AssessmentTime IS NOT NULL
AND (DATE(ofi.AssessmentBaseTime) = dd. OR DATE(ofi.AssessmentTime) = dd.)
AND (ofi.IsSuccess = 0 OR ofi.FirstSuccessDate_UTC5 <= dd.)
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.LabelRetrievedDate_UTC5 <= dd.
AND ofi.RequestCreatedDate_UTC5 <= dd.
AND (ofi.IsSuccess = 0 OR ofi.FirstSuccessDate_UTC5 > dd.)
THEN ofi.RequestId
END) AS ,
-- 24小时换单成功数当天应该换单数中、首次成功时间在考核基准时间与考核时间之间、且完成时间是当天的订单数
COUNT(DISTINCT CASE
WHEN ofi.AssessmentTime IS NOT NULL
AND (DATE(ofi.AssessmentBaseTime) = dd. OR 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., ofi.CustomerCode -- 按日期+客户代码分组
)
-- 最终输出
SELECT
dcm.CustomerCode, -- 客户代码作为第一列
dcm.,
dcm.,
dcm.,
dcm.,
COALESCE(cdsc., 0) AS ,
COALESCE(cdfc., 0) AS ,
COALESCE(cdstop.STOP数, 0) AS STOP数,
dcm.24H完成数 AS 24,
dcm.,
COALESCE(cdscan., 0) AS ,
-- 当天换单完成率:当日换单完成数 / 当天应该换单数
CASE
WHEN dcm. = 0 THEN '0.00%'
ELSE CONCAT(ROUND(COALESCE(cdsc., 0) / dcm. * 100, 2), '%')
END AS ,
-- 24小时换单率考核时间内完成数 / 当天应该考核的订单总数
CASE
WHEN dcm. = 0 THEN '0.00%'
ELSE CONCAT(ROUND(dcm.24H完成数 / dcm. * 100, 2), '%')
END AS 24,
-- 数据拉取时间
(UTC_TIMESTAMP() - INTERVAL 5 HOUR) AS UTC_5
FROM DailyCustomerMetrics dcm
LEFT JOIN CustomerDailySuccessCount cdsc ON dcm. = cdsc. AND dcm.CustomerCode = cdsc.CustomerCode
LEFT JOIN CustomerDailyFailCount cdfc ON dcm. = cdfc. AND dcm.CustomerCode = cdfc.CustomerCode
LEFT JOIN CustomerDailyScanCount cdscan ON dcm. = cdscan. AND dcm.CustomerCode = cdscan.CustomerCode
LEFT JOIN CustomerDailyStopCount cdstop ON dcm. = cdstop. AND dcm.CustomerCode = cdstop.CustomerCode
WHERE dcm.CustomerCode IS NOT NULL -- 过滤无客户代码的订单
ORDER BY dcm. DESC, dcm.CustomerCode;