SQL中的树深度

时间:2009-08-25 20:36:27

标签: sql optimization query-optimization

假设有一个名为tree_node的表,它有一个名为id的主键,并且有一个名为parent_id的可为空的列,并且该表嵌入了一个树结构(或森林),如何在SQL中以有效的方式计算树/林中节点的深度?

4 个答案:

答案 0 :(得分:6)

您需要递归功能。使用递归查询,这将是:

WITH RECURSIVE node_ancestors(node_id, parent_id) AS (
        SELECT id, id FROM tree_node WHERE id IN (1, 2, 3)
    UNION ALL
        SELECT na.node_id, tree_node.parent_id
            FROM node_ancestors AS na, tree_node
            WHERE tree_node.id = na.parent_id AND tree_node.parent_id IS NOT NULL
)
SELECT node_id, COUNT(parent_id) AS depth FROM node_ancestors GROUP BY node_id;

其他选项是在存储过程中进行递归,在应用程序中执行递归并限制递归量并使用大量连接。 (对于非平凡的深度,最后一个选项并不真正有效)

答案 1 :(得分:2)

处理树的常用方法是,除了KEY和PARENT的常规列之外,还有一个PATH类型的列,其中包含一个字符串值,包含构成路径的节点的键,来自根,直到并包括节点本身,由一个不允许成为密钥本身一部分的字符分隔。

让我举个例子:

KEY        PARENT         PATH
1          -              *1*
  2        1              *1*2*
  3        1              *1*3*
    4      3              *1*3*4*
  5        1              *1*5*
    6      5              *1*5*6*

这主要用于不会发生太大变化的树,例如部门层次结构或类似的。

我知道这样的字符串并不完全遵循规范化理论,因为它似乎使多个规则(多键,多值字段等)无效,但它在许多场景中非常有用,包括一个你在要求。

在您的情况下,您只需检索相关节点的TREE值,并根据最简单的方法,计算分隔符的数量,或使用替换函数删除它们,并计算长度的差异

这是SQL为您提供上述节点列表及其深度:

select KEY, PARENT, LEN(PATH)-LEN(REPLACE(PATH, '*', ''))-1 as DEPTH
from NODES

请注意,此方法不需要数据库引擎的任何特殊语法或支持递归SQL,并且非常适合索引。

答案 2 :(得分:1)

如果深度是无限的,则问题是递归的,并且实际上并不是一个简单而有效的解决方案。 (可能有简单的xor高效解决方案。)如果您可以控制模式,并且需要定期访问此类数据,那么您可以将表重构为嵌套集,每个元素都有左右包含边界。这将允许您在具有基本条件的单个查询中计算节点的深度,大约:

select count(*) from tree_node
    where left < myLeft
    and right > myRight

答案 3 :(得分:0)

这不是一个非常聪明的解决方案,但是可以在没有任何其他列且没有递归功能的情况下使用。

您可以像这样进行左联接:

select  p10.ParentId as parent10_id,
        p9.ParentId as parent9_id,
        p8.ParentId as parent8_id,
        p7.ParentId as parent7_id,
        p6.ParentId as parent6_id,          
        p5.ParentId as parent5_id,
        p4.ParentId as parent4_id,
        p3.ParentId as parent3_id,
        p2.ParentId as parent2_id,
        p1.ParentId as ParentId,
        p1.id as id
from        tree_node p1
left join   tree_node p2 on p2.id = p1.ParentId 
left join   tree_node p3 on p3.id = p2.ParentId 
left join   tree_node p4 on p4.id = p3.ParentId  
left join   tree_node p5 on p5.id = p4.ParentId  
left join   tree_node p6 on p6.id = p5.ParentId
left join   tree_node p7 on p7.id = p6.ParentId
left join   tree_node p8 on p8.id = p7.ParentId
left join   tree_node p9 on p9.id = p8.ParentId
left join   tree_node p10 on p10.id = p9.ParentId
order       by 1, 2, 3, 4, 5, 6, 7, 8, 9, 10;

然后检查最后一列中的非空值。如果第8列是第一个只有空值的列,则深度为7。

这绝对不是一个很好的解决方案,但应该适用于所有数据库。