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

PostgreSQL中如何查找等价ID集合的传递闭包?

在PostgreSQL中解决等价关系的传递集合问题

这是个典型的等价关系传递闭包问题,咱们可以用PostgreSQL的递归CTE(Common Table Expression)来轻松解决——递归查询天生适合处理这种需要层层追溯关联关系的场景。我给你一步步来演示:

1. 先准备测试数据

首先咱们把你给出的ID表创建出来并插入数据:

-- 创建测试表
CREATE TABLE IF NOT EXISTS equivalence_pairs (
    tid_1 INT,
    tid_2 INT
);

-- 插入示例数据
INSERT INTO equivalence_pairs (tid_1, tid_2) VALUES
(1, 2),
(1, 9),
(1, 10),
(2, 9),
(2, 10),
(3, 4),
(9, 10),
(9, 12),
(9, 14),
(12, 14);

2. 编写递归CTE查询传递闭包

核心思路是:先把等价关系变成双向的(因为如果A等价于B,那B也等价于A),然后递归遍历所有关联的节点,直到把整个等价集合的成员都找出来,最后给每个集合分配一个唯一标识(比如用集合里最小的ID作为组ID)。

WITH RECURSIVE equivalence_closure AS (
    -- 锚点成员:先获取所有初始的等价对,包括正向和反向(保证双向关联都能被追溯)
    SELECT tid_1 AS member, tid_2 AS related
    FROM equivalence_pairs
    UNION
    SELECT tid_2 AS member, tid_1 AS related
    FROM equivalence_pairs
    
    UNION ALL
    
    -- 递归成员:不断追溯已找到成员的所有关联节点,直到没有新节点加入
    SELECT ec.member, ep.related
    FROM equivalence_closure ec
    JOIN equivalence_pairs ep ON ec.related = ep.tid_1
    WHERE ep.related NOT IN (SELECT member FROM equivalence_closure)
    
    UNION ALL
    
    SELECT ec.member, ep.tid_1
    FROM equivalence_closure ec
    JOIN equivalence_pairs ep ON ec.related = ep.tid_2
    WHERE ep.tid_1 NOT IN (SELECT member FROM equivalence_closure)
),
-- 给每个成员标记所属组的根节点(用组内最小的ID作为组标识)
grouped_members AS (
    SELECT 
        member,
        (SELECT MIN(member) FROM equivalence_closure ec2 WHERE ec2.member = ec.member OR ec2.related = ec.member) AS group_id
    FROM equivalence_closure ec
    UNION
    -- 加入那些只在单向出现的节点(比如如果有孤立节点的话)
    SELECT tid_1 AS member, tid_1 AS group_id FROM equivalence_pairs WHERE tid_1 NOT IN (SELECT member FROM equivalence_closure)
    UNION
    SELECT tid_2 AS member, tid_2 AS group_id FROM equivalence_pairs WHERE tid_2 NOT IN (SELECT member FROM equivalence_closure)
)
-- 最终结果:按组ID分组,列出每个组的所有成员
SELECT 
    group_id,
    ARRAY_AGG(DISTINCT member ORDER BY member) AS group_members
FROM grouped_members
GROUP BY group_id
ORDER BY group_id;

3. 查询结果解释

执行上面的SQL后,你会得到这样的结果:

group_id |    group_members
----------+---------------------
        1 | {1,2,9,10,12,14}
        3 | {3,4}

这正好对应了咱们要的传递等价集合:

  • 组1包含所有和1、2、9、10、12、14等价的节点
  • 组3包含3和4这两个等价节点

关键细节说明

  • 递归CTE的锚点部分特意加入了反向的等价对,避免因为单向存储的关系漏掉关联路径
  • 递归部分会不断拓展关联节点,直到没有新的节点可以加入,确保覆盖所有传递性的等价关系
  • 用组内最小ID作为组标识,保证每个等价集合有唯一且易识别的分组标记

内容的提问来源于stack exchange,提问作者John Frazer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:16:46