【问题标题】:Postgres - How to achieve UNION behaviour with UNION ALL?Postgres - 如何使用 UNION ALL 实现 UNION 行为?
【发布时间】:2021-11-14 18:49:10
【问题描述】:

我有一张带有parentchild ids 的桌子。

create table if not exists stack (
    parent int,
    child int
)

每个父母可以有多个孩子,每个孩子可以再有多个孩子。

insert into stack (parent, child) values
    (1,2),
    (2,3),
    (3,4),
    (4,5),
    (5,6),
    (6,7),
    (7,8),
    (8,9),
    (9,null),
    (1,7),
    (7,8),
    (8,9),
    (9,null);

数据看起来像这样。

|parent|child|
|------|-----|
|1     |2    |
|2     |3    |
|3     |4    |
|4     |5    |
|5     |6    |
|6     |7    |
|7     |8    |
|8     |9    |
|9     |NULL |
|1     |7    |
|7     |8    |
|8     |9    |
|9     |NULL |

我想找到所有孩子。我可以使用带有 UNION ALL 的递归 cte。

with recursive cte as (
select
    child
from
    stack
where
      stack.parent = 1
union
select
    stack.child
from
    cte
left join stack on
    cte.child = stack.parent
where
    cte.child is not null
  )
select * from cte;

这给了我想要达到的结果。

|child|
|-----|
|2    |
|7    |
|3    |
|8    |
|4    |
|9    |
|5    |
|NULL |
|6    |

但是我想包括深度/级别以及每个节点的路径。我可以使用不同的递归 cte 来做到这一点。

with recursive cte as (
select
    parent,
    child,
    0 as level,
    array[parent,
    child] as path
from
    stack
where
      stack.parent = 1
union all
select
    stack.parent,
    stack.child,
    cte.level + 1,
    cte.path || stack.child
from
    cte
left join stack on
    cte.child = stack.parent
where
    cte.child is not null
  )
select * from cte;

这给了我这些数据。

|parent|child|level|path                |
|------|-----|-----|--------------------|
|1     |2    |0    |{1,2}               |
|1     |7    |0    |{1,7}               |
|2     |3    |1    |{1,2,3}             |
|7     |8    |1    |{1,7,8}             |
|7     |8    |1    |{1,7,8}             |
|3     |4    |2    |{1,2,3,4}           |
|8     |9    |2    |{1,7,8,9}           |
|8     |9    |2    |{1,7,8,9}           |
|8     |9    |2    |{1,7,8,9}           |
|8     |9    |2    |{1,7,8,9}           |
|4     |5    |3    |{1,2,3,4,5}         |
|9     |     |3    |{1,7,8,9,}          |
|9     |     |3    |{1,7,8,9,}          |
|9     |     |3    |{1,7,8,9,}          |
|9     |     |3    |{1,7,8,9,}          |
|9     |     |3    |{1,7,8,9,}          |
|9     |     |3    |{1,7,8,9,}          |
|9     |     |3    |{1,7,8,9,}          |
|9     |     |3    |{1,7,8,9,}          |
|5     |6    |4    |{1,2,3,4,5,6}       |
|6     |7    |5    |{1,2,3,4,5,6,7}     |
|7     |8    |6    |{1,2,3,4,5,6,7,8}   |
|7     |8    |6    |{1,2,3,4,5,6,7,8}   |
|8     |9    |7    |{1,2,3,4,5,6,7,8,9} |
|8     |9    |7    |{1,2,3,4,5,6,7,8,9} |
|8     |9    |7    |{1,2,3,4,5,6,7,8,9} |
|8     |9    |7    |{1,2,3,4,5,6,7,8,9} |
|9     |     |8    |{1,2,3,4,5,6,7,8,9,}|
|9     |     |8    |{1,2,3,4,5,6,7,8,9,}|
|9     |     |8    |{1,2,3,4,5,6,7,8,9,}|
|9     |     |8    |{1,2,3,4,5,6,7,8,9,}|
|9     |     |8    |{1,2,3,4,5,6,7,8,9,}|
|9     |     |8    |{1,2,3,4,5,6,7,8,9,}|
|9     |     |8    |{1,2,3,4,5,6,7,8,9,}|
|9     |     |8    |{1,2,3,4,5,6,7,8,9,}|

我的问题是我有很多重复的数据。我想得到与UNION 查询相同的结果,但需要级别和路径。

我尝试了类似的东西

where
    cte.child is not null
    and stack.parent not in (cte.parent)

where
    cte.child is not null
    and not exists (select parent from cte where cte.parent = stack.parent)

但第一个没有改变任何东西,第二个返回错误。

ERROR: recursive reference to query "cte" must not appear within a subquery

有什么想法吗?非常感谢!

【问题讨论】:

  • 您无法获得相同的结果,因为存在多个路径,例如 1..9。您需要决定您更喜欢哪条路径最长、最短和其他标准。

标签: sql postgresql recursion


【解决方案1】:

您的问题是表格数据不合适。例如,您的表包含 8 是 7 的直接子代的信息两次。我建议您删除重复数据并对对实施唯一约束。

如果由于某种原因您不能这样做,请在查询中使行不同:

with recursive
  good_stack as (select distinct * from stack)
 ,cte as 
  (
    select
      parent,
      child,
      0 as level,
      array[parent,
      child] as path
    from good_stack
    where good_stack.parent = 1
    union all
    select
      good_stack.parent,
      good_stack.child,
      cte.level + 1,
      cte.path || good_stack.child
    from cte
    left join good_stack on cte.child = good_stack.parent
    where cte.child is not null and good_stack.child is not null
  )
select * from cte;

演示:https://dbfiddle.uk/?rdbms=postgres_13&fiddle=acb1d7a1a1d26c3fd9caf0e7dedc12b2

(您也可以使列不可为空。条目 9|null 不添加任何信息。如果表缺少这些条目,则 9 仍然没有子项。)

【讨论】:

    猜你喜欢
    • 2011-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-11
    • 2021-07-13
    相关资源
    最近更新 更多