首页 > 解决方案 > sql - 在具有多个相同路径的闭包表中删除

问题描述

我有以下层次结构:

A -> E -> C -> D
|
|
|-> B -> D

这是我想出的关闭表:

| Ancestor | Descendant | Depth |
|    A     |     A      |   0   |
|    B     |     B      |   0   | 
|    C     |     C      |   0   | 
|    D     |     D      |   0   | 
|    E     |     E      |   0   | 
|    A     |     E      |   1   | 
|    A     |     B      |   1   | 
|    A     |     C      |   2   | 
|    E     |     C      |   1   | 
|    A     |     D      |   3   | 
|    E     |     D      |   2   | 
|    C     |     D      |   1   | 
|    A     |     D      |   2   | 
|    B     |     D      |   1   | 

我想删除 and 之间的链接BD因此我想删除 and 之间的链接AD深度之一2)。问题是我不想删除 和 之间的链接,A因为D3没有删除 和 之间的C链接D

目前,这里是列出我要删除的链接的 SQL 语句:

SELECT link.ancestor, link.descendant, link.depth
FROM closure_table p,
     closure_table link,
     closure_table c
WHERE p.ancestor = link.ancestor
  AND c.descendant = link.descendant
  AND p.descendant = B
  AND c.ancestor = D;

但是这个语句给了我不想删除的行:

| Ancestor | Descendant | Depth |
|    A     |     D      |   2   |
|    A     |     D      |   3   |  <- As said before, I want to keep this one
|    B     |     D      |   1   | 

标签: sqlpostgresqltransitive-closure-table

解决方案


您可以选择所有相同祖先-后代对中深度最小的祖先-后代对:

with edges(s, e) as (
   -- the pairs to be removed
   select 'A', 'D'
   union all
   select 'B', 'D'
),
n_l as (
   select c.* from closure c where c.ancestor != c.descendant
)
select c.* from n_l c where exists (select 1 from edges e where e.s = c.ancestor and e.e = c.descendant) 
and c.depth = (select min(c1.depth) from n_l c1 where c1.ancestor = c.ancestor and c1.descendant = c.descendant);

输出:

祖先 后裔 深度
一种 D 2
D 1

推荐阅读