在SQL中,如何解决传递集成员资格?

时间:2018-05-08 14:59:22

标签: sql postgresql

鉴于此ID表:

╔═══════╤═══════╗
║ tid_1 │ tid_2 ║
╠═══════╪═══════╣
║     1 │     2 ║
║     1 │     9 ║
║     1 │    10 ║
║     2 │     9 ║
║     2 │    10 ║
║     3 │     4 ║
║     9 │    10 ║
║     9 │    12 ║
║     9 │    14 ║
║    12 │    14 ║
╚═══════╧═══════╝

并假设每一行都有被认为是等价的集合的标签,我怎样才能(在PostgreSQL中)找到等价的传递集合?

换句话说,我知道方框14中的东西可以与之一起抛出 方框12中的东西;方框12中的东西与方框9中的东西;和方框9, 反过来,与第2和第1栏中的内容没有什么不同。

然后我想去分配一个新的设置ID(例如使用最小的ID) 传递组)所以我得到了

╔═══════╤═══════╗
║   tid │   sid ║
╠═══════╪═══════╣
║     1 │     1 ║
║     2 │     1 ║
║     3 │     3 ║
║     4 │     3 ║
║     9 │     1 ║
║    10 │     1 ║
║    12 │     1 ║
║    14 │     1 ║
╚═══════╧═══════╝

我已经设法达到这一点,我相信这是解决方案的一部分:

create view transitive_closure as (
  with recursive containment as ( select
    p1.tid_1                          as tid_1,
    p1.tid_2                          as tid_2,
    array[ p1.tid_1 ]                 as chain,
    false                             as is_cyclic
  from links                          as p1
union all
select distinct
    p2.tid_1                          as tid_1,
    p2.tid_2                          as tid_2,
    tc.chain || p2.tid_1              as chain,
    p2.tid_2 = any( chain )           as is_cyclic
  from links as p2
  join containment as tc on ( p2.tid_1 = tc.tid_2 )
  )
select distinct tid_1, tid_3 as tid_2 from containment );

1 个答案:

答案 0 :(得分:1)

是的,你非常接近你只需要检查并在你的递归查询中执行最低的tid,将递归查询的前半部分限制到根节点,在第二个中使用is_cyclic检查作为停止条件一半,最后输出tid_1和tid_2列的联合以及sid:

<强> SQL Fiddle

with recursive containment as ( 
  select p1.tid_1 as tid_1,
         p1.tid_2 as tid_2,
         case when p1.tid_1 < p1.tid_2
              then p1.tid_1
              else p1.tid_2
          end sid,
          array[ p1.tid_1 ] as chain,
          false as is_cyclic
  from links as p1
 where not exists (select 1 from links l where l.tid_2 = p1.tid_1)
union all
  select p2.tid_1 as tid_1,
         p2.tid_2 as tid_2,
         case when tc.sid < p2.tid_1
               and tc.sid < p2.tid_2
              then tc.sid
              when p2.tid_1 < p2.tid_2
              then p2.tid_1
              else p2.tid_2
         end sid,
         tc.chain || p2.tid_1 as chain,
         p2.tid_2 = any( chain ) as is_cyclic
    from links as p2
    join containment as tc on ( p2.tid_1 = tc.tid_2 )
   where not tc.is_cyclic
)
select tid_1, sid from containment
union
select tid_2, sid from containment

<强> Results

| tid_1 | sid |
|-------|-----|
|     1 |   1 |
|     2 |   1 |
|     3 |   3 |
|     4 |   3 |
|     9 |   1 |
|    10 |   1 |
|    12 |   1 |
|    14 |   1 |