SQL如何基于参考表递归实现指定日期间隔内的匹配数据分组
SQL实现递归规则分组方案
这类不限深度的递归链路归组需求,本质是求解无向图的连通分量,标准SQL可以直接用*递归公共表表达式(Recursive CTE)*实现,主流数据库(MySQL 8.0+、PostgreSQL、SQL Server、Oracle、Spark SQL等)都支持该语法,具体实现逻辑如下:
- 第一步:先筛选所有符合规则的直接匹配关系,构造无向边
- 第二步:递归遍历所有连通的节点链路,同一条链路内的节点归为同一组
- 第三步:给每组分配统一的Group_Id即可
具体实现代码
以示例数据为例,X取值为4天,代码如下:
WITH RECURSIVE -- 1. 基础数据源表 source_data AS ( SELECT 1 AS id, 'A' AS val, DATE '2022-01-01' AS data_date UNION ALL SELECT 2, 'B', '2022-01-05' UNION ALL SELECT 3, 'C', '2022-01-09' UNION ALL SELECT 4, 'D', '2022-01-31' UNION ALL SELECT 5, 'E', '2022-02-01' ), -- 2. 参考匹配规则表 match_rule AS ( SELECT 'B' AS target_val, 'A' AS matching_val, DATE '2022-01-04' AS valid_start, DATE '2022-01-06' AS valid_end UNION ALL SELECT 'C', 'B', '2022-01-09', '2022-01-09' UNION ALL SELECT 'D', 'A', '2022-01-31', '2022-01-31' ), -- 3. 筛选所有合法直连边(无向,避免单方向关联漏链路) valid_edge AS ( SELECT DISTINCT CASE WHEN s1.id < s2.id THEN s1.id ELSE s2.id END AS node1, CASE WHEN s1.id < s2.id THEN s2.id ELSE s1.id END AS node2 FROM source_data s1 JOIN match_rule r ON s1.val IN (r.target_val, r.matching_val) JOIN source_data s2 ON (s1.val = r.target_val AND s2.val = r.matching_val) OR (s1.val = r.matching_val AND s2.val = r.target_val) WHERE -- 两条数据的日期都在规则有效期内 s1.data_date BETWEEN r.valid_start AND r.valid_end AND s2.data_date BETWEEN r.valid_start AND r.valid_end -- 日期差不超过阈值X=4天 AND ABS(DATEDIFF(s1.data_date, s2.data_date)) <= 4 ), -- 4. 递归遍历所有连通节点 recursive_traverse AS ( -- 锚点:每个节点初始以自身为遍历起点,自身id为初始分组id SELECT id AS current_node, id AS group_id FROM source_data UNION ALL -- 递归扩展:沿着直连边访问未遍历的连通节点,同步更新分组id为链路内最小值 SELECT e.node2 AS current_node, LEAST(rt.group_id, e.node2) AS group_id FROM recursive_traverse rt JOIN valid_edge e ON rt.current_node = e.node1 WHERE e.node2 > rt.current_node -- 规避回头遍历导致的死循环 ) -- 5. 最终输出分组映射,同组取最小id作为Group_Id SELECT MIN(group_id) AS group_id, current_node AS id FROM recursive_traverse GROUP BY current_node ORDER BY id;
结果校验
运行上述代码得到的输出完全匹配需求预期:
- Id=1、2、3同组:1和2间隔4天符合B-A匹配规则,2和3间隔4天符合C-B匹配规则,递归连通
- Id=4单独成组:和Id=1间隔30天,超过4天阈值,不满足连通条件
- Id=5单独成组:无对应匹配规则,不与任何节点连通
注意:如果使用不支持Recursive CTE的老旧SQL版本(如MySQL 5.x、SQL Server 2005之前版本),可以通过循环迭代临时表的方式模拟递归遍历过程,核心逻辑和上述方案完全一致,仅语法写法有区别。
内容的提问来源于stack exchange,提问作者SeaChange
相关产品推荐
相关产品推荐

