从 SQL 到 SPL:组内查找最近的匹配记录

MSSQL 数据库的表 mytable,每个 ID 有一个 ConfirmationStarted 和多个 Closed 状态。

|-------------------------|------|---------------------|
| CreatedAt | ID | NewStatus |
| 2022-05-25 23:17:44.000 | 147 | Active |
| 2022-05-28 05:59:02.000 | 147 | Closed |
| 2022-05-30 20:48:53.000 | 147 | Active |
| 2022-06-18 05:59:01.000 | 147 | Closed |
| 2022-06-21 20:09:48.000 | 147 | Active |
| 2022-06-25 05:59:01.000 | 147 | Closed |
| 2022-07-13 00:02:47.000 | 147 | ConfirmationStarted |
| 2022-07-15 15:33:30.000 | 147 | ConfirmationDone |
| 2022-08-25 05:59:01.000 | 147 | Closed |
| 2023-03-08 13:34:57.000 | 1645 | Draft |
| 2023-03-22 19:58:51.000 | 1645 | Active |
| 2023-04-29 05:59:02.000 | 1645 | Closed |
| 2023-05-08 14:50:29.000 | 1645 | Awarded |
| 2023-05-08 14:53:34.000 | 1645 | ConfirmationStarted |
| 2023-05-08 17:53:55.000 | 1645 | ConfirmationDone |

现在要在每个 ID 里,找到 ConfirmationStarted 之前的所有的 Closed 中,离 ConfirmationStarted 最近的那条记录,取出记录的 ID 和时间字段。

|------|-------------------------|
| ID | xdate |
| 147 | 2022-06-25 05:59:01.000 |
| 1645 | 2023-04-29 05:59:02.000 |

SQL 解法:

复制代码
With cte AS (
    SELECT ID, CreatedAt, NewStatus,
        ROW_NUMBER() OVER (PARTITION BY ID ORDER BY CreatedAt DESC) AS rn
    FROM mytable
    WHERE NewStatus = 'Closed'
    AND CreatedAt < (
        SELECT CreatedAt FROM mytable AS sub
        WHERE sub.ID = mytable.ID AND sub.NewStatus = 'ConfirmationStarted'
    )
)
SELECT ID, CreatedAt as xdate
FROM cte
WHERE rn = 1
ORDER BY ID;

SQL没有天然序号,需要先用窗口函数生成序号。SQL分组后必须立刻汇总,不能对组内记录进行过滤,只能绕道用多层子查询反复过滤。整体代码有点繁琐又难懂。

SPL有天然序号,还提供有丰富的与位置相关的计算。SPL分组后可以保持分组子集,便于处理组内数据。

|---|------------------------------------------------------------------------------------|
| |  A |
| 1 | "select ID,CreatedAt,NewStatus from mytable order by CreatedAt")=mssql.query( |
| 2 | =A1.group(ID) |
| 3 | =A2.(~.select@c(NewStatus!="ConfirmationStarted").select@z1(NewStatus=="Closed")) |
| 4 | =A3.new(ID,CreatedAt:xdate) |

A1:从数据库加载数据,按时间排序。

A2:按 ID 分组,但不汇总。

A3:过滤每组数据,先找到 ConfirmationStarted 之前的记录,再从中过滤出 Closed,取倒数第 1 条。函数 select 用于条件过滤,过滤时支持与位置相关的计算,@c 表示从第一个使条件为真的记录开始取,直到遇到使条件为假的记录时停止,@1 表示取结果的第 1 条,@z 表示从后往前过滤。

A2-A4 可以合成一句:=A1.group(ID;~.select@c(NewStatus!="ConfirmationStarted").select@z1(NewStatus=="Closed").CreatedAt:xdate)

SPL已开源免费,欢迎前往乾学院了解更多!

免费下载

相关推荐
一只积极向上的小咸鱼5 分钟前
第一性原理思考:从拆解问题到用实验检验答案
数据库
这个DBA有点耶34 分钟前
时序数据库InfluxDB高基数问题深度拆解:内存索引膨胀机制与OOM完整链路
数据库·架构·dba
加油yx1 小时前
MySQL数据类型
数据库·mysql
计算机编程指导师1 小时前
【计算机毕设选题推荐】基于Hadoop的宫颈癌变进程光谱数据分析系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
大数据·hadoop·python·数据分析·spark·毕业设计·宫颈癌变
蓝胖的四次元口袋1 小时前
Redis知识梳理(1)
数据库·redis·缓存
夜雪一千1 小时前
MySQL 数据加密实战:从传输、存储到字段级加密,分清场景不踩坑
数据库·mysql
wefg12 小时前
【Redis】C++ 自定义客户端
数据库·redis·缓存
可乐ea2 小时前
小模型也能做数据分析智能体:把 harness 收窄做小
数据挖掘·数据分析·ai智能体·上下文工程·数据分析智能体·agentharness·工具调用设计
这个DBA有点耶2 小时前
为什么UPDATE比SELECT危险得多?锁范围等于扫描行数
数据库·架构·代码规范
天国梦3 小时前
国密门禁品牌怎么选?2026年最新3个避坑要点
运维·服务器·数据库·python