树:计算2条路径的成本并确定哪条路径更贵

时间:2012-04-05 08:17:54

标签: sql postgresql recursion tree common-table-expression

这是我在这个论坛上的第一个问题,所以我会尽量保持清楚。

我有1个表entity,其中包含以下数据:

ATTR1                ATTR2                 ATTR3                 ATTR4

A                    Level 1                null                   35
B                    Level 2                 A                     34
C                    Level 2                 A                     33
D                    Level 3                 B                     32
E                    Level 3                 B                     31
F                    Level 3                 C                     30
G                    Level 3                 C                     29
H                    Level 4                 D                     28
I                    Level 4                 D                     27
J                    Level 4                 E                     26
K                    Level 4                 E                     25
L                    Level 4                 F                     24
M                    Level 4                 F                     23
N                    Level 4                 G                     22
O                    Level 4                 G                     21
P                    Level 5                 H                     20
Q                    Level 5                 H                     19
R                    Level 5                 H                     18
S                    Level 5                 O                     17

其中ATTR1是节点的名称。它也是主要的关键 其中ATTR2是节点的级别 其中ATTR3是节点父节点的名称。 A是根,它没有父节点,因此NULL 其中ATTR4是节点的成本。

现在的问题是:

  • 给定任何部分X和叶子节点Y(即Y是X的后代),从根到X或从X到Y的直接后代最昂贵的路径是什么?

换句话说,假设X节点为D,Y节点为P。从节点到根的路径为D-B-A,而从叶到节点的路径为P-H-D

如何计算每条路径的总成本并且能够说哪个更昂贵?

我的方法是做2个递归查询,每个路径有1个查询来查找每个查询的SUM。问题是我被迫创建了2个表并尝试将所有数据都放在1​​中。我觉得我已经走到了尽头,它开始看起来有点长而且不可行。

感谢任何帮助,最好是PostgreSQL语法。

2 个答案:

答案 0 :(得分:2)

创建这样的表:

create table entity (attr1 text not null primary key,
                     attr2 text not null,
                     attr3 text,
                     attr4 int not null);

...并用上面显示的数据填充它,你在找这样的东西吗?:

with recursive cst as (
with req as (
select 'A'::text as top, 'D'::text as bottom
union all
select 'D'::text, 'P'::text
)
select
    top,
    bottom,
    top as last,
    top as path,
    attr4 as cost
  from req
  join entity on (top = attr1)
union
select
    top,
    bottom,
    attr1,
    path || '-' || attr1,
    cost + attr4
  from cst
  join entity on (attr3 = last)
), res as (
select * from cst where bottom = last
)
select path from res
   where cost = (select max(cost) from res);

当然,req CTE作为一种指定请求的方式是一种破解,但我相信你可以按照你想要的那样完成那部分。此外,这总是显示从“上部”到“下部”而不是“外部”到“内部”的路径,但我不确定这对您来说是否重要。无论如何,我认为这应该足够接近你想要的东西。

答案 1 :(得分:0)

首先,将树的级别保存为integer而不是(冗余且不合适)text
该表如下所示:

CREATE TABLE entity (
  name   text NOT NULL PRIMARY KEY
 ,level  int  NOT NULL
 ,parent text
 ,cost   int  NOT NULL);

查询:

WITH RECURSIVE val(root, leaf) AS (
    VALUES                          -- provide values here
     ('A'::text, 'D'::text)
    ,('D',       'P')
    ), x AS (
    SELECT v.root   AS name
          ,v.root   AS path
          ,r.cost   AS total
          ,1        AS path_len
          ,l.level - r.level AS len -- as break condition
    FROM   val    v
    JOIN   entity r ON r.name = root
    JOIN   entity l ON l.name = leaf

    UNION  ALL
    SELECT e.name                -- AS parent
          ,x.path || '-' || e.name -- AS path
          ,x.total + e.cost      -- AS total
          ,x.path_len + 1        -- AS path_len
          ,x.len                 -- AS len
    FROM   x
    JOIN   entity e ON e.parent = x.name
    WHERE  x.path_len <= x.len
    )
SELECT x.path, x.total
FROM   x
JOIN   val v ON x.name = v.leaf AND x.path_len > 1
ORDER  BY x.total DESC
LIMIT  1;

结果:

path  | total
------+-------
A-B-D | 101

Demo at sqlfiddle.

重点

  • VALUES提供价值更快/更简单/更直观。

  • 使用UNION ALL代替UNION,否则递归联合必须检查(在这种情况下不存在)每次迭代重复。

  • 不要在递归CTE中包含rootleaf列,它们是自重的。

  • 不需要嵌套的WITH子句。您可以在WITH RECURSIVE子句中使用普通CTE。

  • 对性能最重要的是:在你的模型中,你事先知道路径的长度。使用它作为中断条件并且不计算所有到苦味末端的路径 - 对于大树来说可能非常昂贵。

  • 最终的SELECT也可以在很大程度上简化,不需要聚合函数。 加入您的价值观并选择正确的道路。这样,您可以根据需要轻松显示结果中的任何或所有列。