You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 09:39:43