Sql 树:计算两条路径的成本,并确定哪条路径更昂贵
这是我在这个论坛上的第一个问题,所以我会尽量保持清晰 我有一个表Sql 树:计算两条路径的成本,并确定哪条路径更昂贵,sql,postgresql,recursion,tree,common-table-expression,Sql,Postgresql,Recursion,Tree,Common Table Expression,这是我在这个论坛上的第一个问题,所以我会尽量保持清晰 我有一个表实体,包含以下数据: ATTR1 ATTR2 ATTR3 ATTR4 A Level 1 null 35 B Level 2 A
实体,包含以下数据:
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个查询,以找到每个路径的总和。问题是我被迫创建了两个表,并试图将它们的所有数据放在一个表中。我觉得我已经走到了死胡同,它开始看起来有点长,不可行
非常感谢您的帮助,最好使用PostgreSQL语法。创建如下表:
create table entity (attr1 text not null primary key,
attr2 text not null,
attr3 text,
attr4 int not null);
CREATE TABLE entity (
name text NOT NULL PRIMARY KEY
,level int NOT NULL
,parent text
,cost 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作为指定请求的一种方式有点不成熟,但我相信您可以根据需要对该部分进行修改。此外,这总是显示从“上”到“下”的路径,而不是从“外”到“内”,但我不确定这对你是否重要。无论如何,我认为这应该足够接近你想要的东西。首先,将树的级别保存为integer
而不是(冗余和不合适的)文本
该表如下所示:
create table entity (attr1 text not null primary key,
attr2 text not null,
attr3 text,
attr4 int not null);
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;
要点
值
提供值更快/更简单/更直观
- 使用
UNION ALL
而不是UNION
,否则递归联合必须检查每个迭代是否重复(在本例中不存在)
- 不要将
root
和leaf
列包含在递归CTE中,它们是自重
- 不需要嵌套的
WITH
子句。您可以在带有递归
子句的中使用普通CTE
对性能最重要的是:在模型中,您事先知道路径的长度。使用它作为中断条件,不要计算所有通往痛苦终点的路径——对于大树来说,这可能非常昂贵
最后的SELECT
也可以大大简化,不需要聚合函数。
加入你的价值观,选择正确的路径。这样,如果需要,可以轻松显示结果中的任何或所有列
难以置信!那正是我要找的!我还不熟悉递归查询,在问问题之前,我连续几个小时绞尽脑汁。答案的顺序(路径)没有区别,只要它是两条路径中代价较高的一条。多谢各位@postgresdbs:虽然Kevin的查询确实有效,但仍有很大的改进空间。我发布了一个答案。第一个非常有用的问题是+1。所有的信息都在那里,就像它应该在那里一样。