SQL 树结构

SQL Tree Structure

我在 SQL 对这个主题(即树结构)不熟悉。我已经浏览了不同的来源,但仍然不清楚。

在我的案例中,我有一个 table 附在此处。

现在首先,我必须检索“OFFICE”的完整树。

我还必须在附加的分层数据中找到所有叶节点(没有子节点的节点)。

请提供详细解释的答案。 提前致谢。

Now here first, I have to retrieve a Full Tree for “OFFICE”.

您想如何表示您的树?你发的已经可以算作一棵树了

Also I have to find all the leaf nodes (those with no Children) in the attached hierarchical data.

TSQL:

select * 
from table outer 
where id not in (select id 
                 from table inner 
                 where inner.parent = outer.id)

内部的 select 会给你所有指向某个父级的 ID。外部 select 将为您提供内部 select 没有结果的所有节点。

有一个名为 "hardcoded trees" 的模式设计可用于此目的。

然后您可以使用此查询为每个 child

查找 parent
SELECT level1ID FROM Level2entity as L2 WHERE level2ID = :aLevel2ID

或者这个用于为每个 parent 找到 children:

SELECT level1ID FROM Level2entity as L2 WHERE level2ID = :aLevel2ID

如果你能负担得起一点预处理时间,那么这个问题的经典解决方案是使用 nested sets.

CREATE TABLE node (
    id INTEGER NOT NULL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    parent_id INTEGER DEFAULT NULL,
    seq_min INTEGER NOT NULL,
    seq_max INTEGER NOT NULL
); 

假设您使用某种内存数据结构,例如

class Node:
   def __init__(self, id, name, children=()):
      self.id = id
      self.name = name
      self.children = list(children)

表示内存中的树节点,然后我们可以定义

def assign_sequence_numbers(root_node):
    def recurse(counter, node):
        node.seq_min = counter
        node.seq_max = reduce(recurse, node.children, 1 + counter)
        return node.seq_max
    return recurse(1, root_node)

对树进行预序遍历,为每个节点分配两个额外的数字:

seq_min 值成为每个节点的备用唯一整数键。此外,一个节点的所有直接子节点(和间接子节点)都将有一个 seq_min 值,该值大于分配给节点本身的值。

seq_max 值只是所有后代节点的 seq_min 值的上限。

示例:考虑以下树

Root
 |
 +--- Category1
 |      |
 |      +--- Category1.1
 |      |
 |      `--- Category1.2
 |
 `--- Category2

使用上面定义的函数,我们得到

Root [min=1, max=6]
 |
 +--- Category1 [min=2, max=5]
 |      |
 |      +--- Category1.1 [min=3, max=4]
 |      |
 |      `--- Category1.2 [min=4, max=5]
 |
 `--- Category2 [min=5, max=6]

观察,对于每个节点,min 值总是 <= 所有后代的 min 值并且 max 值总是 > 最大值所有后代的价值。因此,要找到 Root 的后代和该节点本身(即获取整棵树),我们会这样做:

SELECT * FROM node 
WHERE seq_min >= 1 /* i.e., seq_min(Root) */
AND seq_min < 6    /* i.e., seq_max(Root) */

获取 Category1 的后代(以及该节点本身)

SELECT * FROM node 
WHERE seq_min >= 2 /* i.e., seq_min(Category1) */
AND seq_min < 5    /* i.e., seq_max(Category2) */

这种方法的缺点是,原则上,如果您对树进行更改,即插入新节点或删除旧节点,则必须为所有节点重新分配 seq_xxx 编号那些。这通常不是问题,至少如果树结构的更改不频繁并且树足够小的话。

由于 ITGenius 已经链接到 that answer:如果您的数据库支持它,使用常见的 table 表达式是另一种可能性,它避免了预处理步骤,并且在面对更改树结构。

(我没有测试此答案中给出的任何代码。您可能需要在这里和那里应用一些修复;特别是,如果您选择的项目语言是 而不是 python...)

您没有指定您的 DBMS,但使用标准 SQL(所有 modern DBMS 都支持)您可以轻松地进行递归查询以获取完整的树:

with recursive full_tree as (
  select id, name, parent, 1 as level
  from departments
  where parent is null
  union all 
  select c.id, c.name, c.parent, p.level + 1
  from departments c
    join full_tree p on c.parent = p.id
)
select *
from full_tree;

如果需要子树,只需要改变普通table表达式中的起始条件即可。得到例如全部 "categories":

with recursive all_categories as (
  select id, name, parent, 1 as level
  from departments
  where id = 2 --- << start with a different node 
  union all 
  select c.id, c.name, c.parent, p.level + 1
  from departments c
    join all_categories p on c.parent = p.id
)
select *
from all_categories;

获取所有叶子很简单:所有节点的 ID 都没有显示为 parent:

select *
from departments
where id not in (select parent
                 from departments
                 where parent is not null);

SQL小提琴示例:http://sqlfiddle.com/#!15/414c9/1


指定 DBMS 后编辑。

Oracle 确实支持递归 CTE(尽管为此您需要 11.2.x),您只需省略关键字 recursive。但您也可以使用 CONNECT BY 运算符:

select id, name, parent, level
from departments
start with parent is null
connect by prior id = parent;

select id, name, parent, level
from departments
start with id = 2
connect by prior id = parent;

SQLOracle 小提琴:http://sqlfiddle.com/#!4/6774ee/3

详见手册:https://docs.oracle.com/database/121/SQLRF/queries003.htm#i2053935