用CASE WHEN实现横向迭代,节点数据串行推算

用CASE WHEN横向迭代,6步推算节点时间

我以为一步SQL能搞定。

结果写了6个临时表,几百行CASE WHEN。

运营把需求甩过来时,我看了一眼表结构,心想:关联静态路由表不就完了?

跑完第一条数据,我就愣住了。


错发的运单,静态路由表没数据

全链路节点时效:从寄件网点到派件网点,每个节点的计划到达时间、计划发出时间。首中心、中转1-5、末中心,7个节点。

我一开始写了个简单关联:

sql 复制代码
SELECT 
    a.ewb_no,
    b.plan_first_center_arr_time,
    b.plan_first_center_send_time,
    b.plan_trans_center_arr_time1,
    b.plan_trans_center_send_time1
FROM dwd.ewb_sub_act_plan_node_time_analysis_h a
LEFT JOIN dw.t_time_rule_ewb_static_route b 
    ON a.plan_full_code = b.route_code

路由一致的运单没问题,计划时间全出来了。

但错发的运单呢?计划路由是"沈阳→高碑店→广州",实际错发成"沈阳→高碑店→武汉→广州"。静态路由表里没有这条路由,LEFT JOIN全NULL。

运营说:错发也要算后续时间,不然没法评估时效。

我盯着那一排NULL,心里开始打鼓------这玩意一步SQL搞不定。


横向迭代:每一步都依赖上一步的结果

我花了一个下午想推算逻辑,画了满桌子的流程图,终于理清了:

首中心计划发出时间 → 中转节点1计划到达时间 → 中转节点1计划发出时间 → 中转节点2计划到达时间 → ...

每个节点的计划发出时间,依赖上一个节点的结果。

这不是聚合,不是关联,是横向迭代------数据在节点间横向流动,上一步的输出是下一步的输入。

SQL天生不擅长迭代。窗口函数只能在同行做计算,跨行迭代做不了。递归CTE理论上可以做,但这个场景每一步的计算逻辑不一样------第2步算首中心的发出时间,第3步算中转节点1的发出时间,每步关联的班次表条件不同,CASE WHEN嵌套也不一样。递归CTE的UNION逻辑写不了这种每步都变的情况。

只能用临时表,一步步串行推算。


6步临时表,串行推算

复制代码
第1步:判断路由是否一致
    ↓
第2步:计算首中心计划发出时间
    ↓
第3步:计算中转节点1计划时间(FROM tmp.step2)
    ↓
第4步:计算中转节点2计划时间(FROM tmp.step3)
    ↓
第5步:计算中转节点3-5计划时间
    ↓
第6步:计算末中心计划时间

6个临时表,串行推算。每一步都依赖上一步的结果,不能跳步,不能并行。

第1步:判断路由是否一致

关联静态路由表,逐节点比对实际路由和计划路由:

sql 复制代码
CREATE TABLE tmp.step1 AS
SELECT 
    a.ewb_no,
    -- 首中心:实际 vs 计划
    CASE WHEN a.act_first_center_site_code = b.delivery_org_code 
        THEN 1 ELSE 0 END AS first_center_flag,
    -- 中转节点1:实际 vs 计划
    CASE WHEN a.trans_center_code1 = b.path_node1_org_code 
        THEN 1 ELSE 0 END AS trans1_flag
FROM dwd.ewb_sub_act_plan_node_time_analysis_h a
LEFT JOIN dw.t_time_rule_ewb_static_route b 
    ON a.plan_full_code = b.route_code

flag=1:路由一致,直接取静态路由表的计划时间。

flag=0:路由不一致,要推算。

第2步:计算首中心计划发出时间

这是横向迭代的起点。

路由一致时,直接取静态路由表的值。

路由不一致时,要根据"计划到达时间 + 卸车时长 + 装车时长"在车线班次表里找最近能赶上的班车。

sql 复制代码
CREATE TABLE tmp.step2 AS
SELECT 
    a.ewb_no,
    CASE 
        WHEN a.first_center_flag = 1 
            THEN a.delivery_org_send_time  -- 路由一致,直接取
        ELSE 找最近班车的逻辑                -- 路由不一致,推算
    END AS plan_first_center_send_time
FROM tmp.step1 a
LEFT JOIN dim.qy_vehicle_route b 
    ON a.act_first_center_site_code = b.departure_org_code

第3步:计算中转节点1的计划时间

这一步用到了第2步的结果------横向迭代的关键

sql 复制代码
CREATE TABLE tmp.step3 AS
SELECT 
    a.ewb_no,
    a.plan_first_center_send_time,  -- 第2步算出来的
    -- 计划到达时间 = 上一步的计划发出时间 + 班次运行时长
    a.plan_first_center_send_time + b.arrive_over_time 
        AS plan_trans_center_arr_time1,
    CASE 
        WHEN a.trans1_flag = 1 
            THEN b.path_node1_org_send_time
        ELSE 找最近班车的逻辑
    END AS plan_trans_center_send_time1
FROM tmp.step2 a  -- 注意:FROM的是第2步的临时表
LEFT JOIN dim.qy_vehicle_route b ON ...

注意FROM------是第2步的临时表tmp.step2plan_first_center_send_time是第2步算出来的结果。

这就是横向迭代:每一步的输入是上一步的输出。

第4-6步:同理推算

逻辑和第3步一样,只是每一步FROM的是更前一步的临时表。


找最近班车:5种情况,CASE WHEN逐层判断

路由不一致时,要在车线班次表(dim_ll_qy_vehicle_route)里找最近能赶上的班车。

班次不是每天都发车。run_period字段决定了发车规则:

  • 隔日班(run_period='8'):隔天发,要看生效日期差值能不能被2整除
  • 周班(run_period='1,2,3...'):逗号分隔的周几数字

原始的run_period是变长的逗号分隔字符串,我用RPAD(run_period,13,',9')填充到13位,变成固定格式方便解析:

原始 run_period RPAD (13 位,右侧补 9) 结构化值
"1,2,3,4,5,6,7" "1,2,3,4,5,6,7"
"1,3,5,7" "1,3,5,7,9,9,9"
"2,4,6" "2,4,6,9,9,9,9"
"8" "8,9,9,9,9,9,9"

9是填充值,表示该位置没有班次。8表示隔日班。

5种情况,一层CASE WHEN套一层:

① 隔日班,今天发不发?

run_period='8'表示隔日班。用生效日期和当前日期的差值,看能不能被2整除:

复制代码
effective_date = 2026-05-01
当前日期 = 2026-05-13,差值 = 12天,12 % 2 = 0 → 今天有班次
当前日期 = 2026-05-14,差值 = 13天,13 % 2 ≠ 0 → 明天才有班次

② 周班,今天有班次,赶得上吗?

run_period='1,3,5'表示周一、三、五发车。

复制代码
今天周三,run_period含3,今天有班次:
  当前发出时间 = 10:00,班次发车 = 14:00 → 赶得上 → 返回当天班次
  当前发出时间 = 16:00,班次发车 = 14:00 → 赶不上 → 返回次日班次

③ 周班,今天没班次,找下一个班次

今天周三(week_day=3),run_period='1,3,5,7',RPAD后是'1,3,5,7,9,9,9'

用SUBSTR逐位解析,找下一个有班次的日子:

复制代码
SUBSTR(run_period,1,1) = '1' → 周一有班次
SUBSTR(run_period,3,1) = '3' → 周三有班次
SUBSTR(run_period,5,1) = '5' → 周五有班次
SUBSTR(run_period,7,1) = '7' → 周日有班次

今天周三,下一个班次是周五(5),差值 = 5 - 3 = 2天
如果今天是周六(6),没有≥6的班次,取最小值(1),差值 = 1 - 6 + 7 = 2天

核心逻辑:

复制代码
run_period里有≥当前星期几的值 → 取最小差值
run_period里都<当前星期几 → 取最小值+7再减当前星期几

④ 汇总

每个节点都要跑一遍这5种情况的判断。6个临时表,每个表里都有这段100多行的CASE WHEN。

写到最后,我自己都快绕晕了。


前几步Presto,最后一步Spark

中间步骤用Presto,纯粹是因为Presto快。6个临时表串行跑,每一步都能秒级出结果,调试方便。

最后一步用Spark,是为了动态分区写入 。目标表按天分区,Presto不支持动态分区,得手动指定分区值。Spark的INSERT OVERWRITE自动按分区字段写入,不用每个分区写一条SQL。


横向迭代的核心:数据在层级间流动,每一步依赖上一步的结果。临时表 + CASE WHEN是SQL里最朴素的解法。

写完最后一个临时表,我盯着屏幕上的6行CREATE TABLE,心想:下次再有人问我横向迭代怎么写,直接甩给他------别想一步算完,SQL做不到。

相关推荐
3D探路人1 小时前
模灵 大模型聚合API 转发流程技术实现
java·大数据·开发语言·前端·人工智能·计算机视觉
城事漫游Molly2 小时前
案例研究:如何明智地选择案例、精巧地界定边界、深刻地进行分析?
大数据·人工智能·ai写作·论文笔记
LaughingZhu2 小时前
Product Hunt 每日热榜 | 2026-05-12
大数据·人工智能·经验分享·神经网络·产品运营
eastyuxiao3 小时前
数字孪生(Digital Twin)从入门到实战教程
大数据·人工智能·数字孪生
皮皮学姐分享-ppx3 小时前
上市公司数字技术风险暴露数据(2010-2024)|《经济研究》同款大模型测算
大数据·网络·数据库·人工智能·chatgpt·制造
数字会议深科技3 小时前
政务表决会议升级方案解析|多形态大型表决系统融合方案科普
大数据·人工智能·政务·无纸化·会议厂商·ai会议生态服务商·表决系统
互联网科技看点3 小时前
泛微・齐业成核心优势深度解析:数智化费控管理标杆
大数据·人工智能·云计算
财经资讯数据_灵砚智能5 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(日间)2026年5月13日
大数据·人工智能·python·信息可视化·自然语言处理
霑潇雨5 小时前
Spark学习基础转换算子案例(单词计数(WordCount))
java·大数据·分布式·学习·spark·maven