本文介绍了PostgreSQL将数据从递归CTE传递到函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到以下问题:我试图发现从源节点( node_s )到目标节点( node_t )的所有可能路径。

带图边的原始表的格式很简单: | node_x | node_y |强度| ,其中node_x - >node_y是边缘强度为weight的直接边缘。 p 在探索路径的任何一点,我们发现其子节点中的一个节点的目标是 node_t ,我们记录此路径并停止探索来自此节点的路径,否则继续探索。 b

简单的解决方案是使用PostgreSQL的递归CTE来构建图的传递闭包:

  WITH RECURSIVE transitive_closure(source,target,weight,path_string)AS 

--non-recurive term
SELECT b.node_x,b.node_y,b.strength,b.node_x || '。'|| b.node_y ||'。'AS path_string
FROM best_path b
WHERE b.node_x = node_s --source_node

UNION

--recurive term
SELECT tc.source,b.node_y,least(tc.weight,b.strength),tc.path_string || b.node_y ||'。'AS path_string
FROM best_p AS AS b JOIN transitive_closure AS tc ON b.node_x = tc.target
WHERE tc.path_string NOT LIKE'%'|| b.node_y || '。%'

SELECT *
FROM transitive_closure tc
WHERE tc.target = node_t --target_node

以上代码将发现源节点 node_s 中的所有可能路径。只有在传递闭包构造之后,我才能够从源节点到目标节点中选择所需的路径行(请参阅上一条SELECT语句)。



示例:



best_path表中包含以下数据:

  node_x | node_y |强度
-------- + -------- + ----------
1 | 2 | 1
1 | 3 | 1
2 | 4 | 1
2 | 5 | 1
3 | 6 | 1
3 | 7 | 1
4 | 8 | 1
4 | 9 | 1
5 | 10 | 1
5 | 11 | 1

查询:



找到路径从源节点= 1到目标节点= 4

结果:

  source |目标|强度| path_string 
-------- + -------- + ---------- + ------------
1 | 2 | 1 | 1.2。
1 | 3 | 1 | 1.3。
1 | 4 | 1 | 1.2.4。
1 | 5 | 1 | 1.2.5。
1 | 6 | 1 | 1.3.6。
1 | 7 | 1 | 1.3.7。
1 | 8 | 1 | 1.2.4.8。
1 | 9 | 1 | 1.2.4.9。
1 | 10 | 1 | 1.2.5.10。
1 | 11 | 1 | 1.2.5.11。

这不是我所需要的。由于节点2到节点4(目标)之间存在直接边缘,因此我不需要路径1.2.5。,1.2.4.8。,1.2.4.9。,1.2.5.10。,1.2.5.11,路径探索对于节点2,应该在发现从2到4的路径时停止。



总而言之,我不想发现节点的路径已经对目标节点有直接的优势。这意味着在CTE的递归术语中,我希望有一些条件可以表示如下,伪代码如下所示:

  WITH RECURSIVE transitive_closure(source,target,weight,path_string)AS 

- non-recurive term(as before)
SELECT b.node_x,b.node_y,b.strength,b .node_x ||'。'|| b.node_y ||'。'AS path_string
FROM best_path b
WHERE b.node_x = node_s --source_node

UNION

- 经过期限
SELECT tc.source,b.node_y,least(tc.weight,b.strength),tc.path_string || b.node_y ||'。'AS path_string
FROM best_path AS b JOIN transitive_closure AS tc ON b.node_x = tc.target
WHERE tc.path_string NOT LIKE'%'|| b.node_y ||'。%'
AND b。 node_y = node_t
- 将只选择直接指向目标的行

UNION(CTE中不允许使用第二个联合)

选择那些不行的行有直接的优势t
以及哪个父母没有参与构建上述查询。
iebnode_x = tc.target b.node_x和node_t之间没有直接的边界b




作为查询从源节点= 1到目标节点= 4的查询的结果,我希望得到以下结果:

 来源|目标|强度| path_string 
-------- + -------- + ---------- + ------------
1 | 2 | 1 | 1.2。
1 | 3 | 1 | 1.3。
1 | 4 | 1 | 1.2.4。
1 | 6 | 1 | 1.3.6。
1 | 7 | 1 | 1.3.7。

预先感谢您的帮助!

我已经尝试了很多方法:例如条件在FROM / WHERE子句中,试图传递CTE到函数中,但没有成功。



任何建议都将不胜感激。



我有我自己的递归函数,可以实现我想要的功能,但是,对于大量的数据来说,它非常慢;并且PostgreSQL的CTE显然是非常优化的,所以我想深入挖掘它。 解决方案

您可以使如果从底部开始,搜索路径的效率会更高。从孩子开始。如果你从父母开始,它需要遍历所有的孩子;而如果你从孩子那里搜索,它只有一个父母,因此不会浪费时间找到源和目标之间的路径。

 <$ c $ (b 



























union all

从tbl中选择i.source,i.target,fp.recentness + 1

在i.target = fp.source中加入find_parent fp
),
construct_path(source,target,recentness,path)as

select source,target,recentness,source ||'。'|| target $ b $ from find_parent
where recentness =(从find_parent中选择max(recentness))

union

选择dd.source,dd.target,dd.recentness,cp.path ||' 。'|| dd.target
from find_parent dd
join construct_path cp on dd.recentness = cp.recentness - 1

选择源,目标,路径
从construct_path
按最近的顺序删除

输出:

 源目标路径
1 2 1.2
2 4 1.2.4
4 9 1.2.4.9

现场测试:



与此类似:


I have the following problem: I am trying to discover all possible paths from source node (node_s) to target node (node_t).

The format of the original table with graph edges is simple: | node_x | node_y | strength | , where "node_x" -> "node_y" is a direct edge with strength of the edge being "weight".

The idea is if at any point during the exploration of the paths we discover that a node among its children has target node_t, we record this path and stop exploring paths from this node, otherwise continue exploration.

The simple solution was to use PostgreSQL's recursive CTE which constructs the transitive closure of the graph:

  WITH RECURSIVE transitive_closure (source, target, weight, path_string) AS
  ( 
   --non-recurive term
   SELECT b.node_x, b.node_y, b.strength, b.node_x || '.' || b.node_y || '.' AS path_string
   FROM best_path b
   WHERE b.node_x = node_s --source_node

   UNION

   --recurive term
   SELECT tc.source, b.node_y, least(tc.weight,b.strength), tc.path_string || b.node_y || '.' AS path_string
   FROM best_path AS b JOIN transitive_closure AS tc ON b.node_x = tc.target
   WHERE tc.path_string NOT LIKE '%' || b.node_y || '.%'
  )
  SELECT * 
  FROM transitive_closure tc
  WHERE tc.target = node_t --target_node

The code above will discover all possible paths from source node node_s. Only after transitive closure construction, I am able to select the needed rows of paths from source node to target node (see last SELECT statement).

Example:

best_path table has the following data:

 node_x | node_y | strength 
--------+--------+----------
      1 |      2 |        1
      1 |      3 |        1
      2 |      4 |        1
      2 |      5 |        1
      3 |      6 |        1
      3 |      7 |        1
      4 |      8 |        1
      4 |      9 |        1
      5 |     10 |        1
      5 |     11 |        1

query:

find the paths from source node = 1, to target node = 4

result:

 source | target | strength | path_string
--------+--------+----------+------------
      1 |      2 |        1 | 1.2.
      1 |      3 |        1 | 1.3.
      1 |      4 |        1 | 1.2.4.
      1 |      5 |        1 | 1.2.5.
      1 |      6 |        1 | 1.3.6.
      1 |      7 |        1 | 1.3.7.
      1 |      8 |        1 | 1.2.4.8.
      1 |      9 |        1 | 1.2.4.9.
      1 |     10 |        1 | 1.2.5.10.
      1 |     11 |        1 | 1.2.5.11.

This is not what I need. Since there is a direct edge from node 2 to node 4 (target) already, I do not need paths 1.2.5., 1.2.4.8., 1.2.4.9.,1.2.5.10.,1.2.5.11., the path exploration for node 2 should stop at the point when the path from 2 to 4 was discovered.

To sum up, I do not want to discover the paths of the node if it already has a direct edge to the target node. This means that in a recursive term of CTE, I would like to have some condition that will say the following, pseudocode follows:

  WITH RECURSIVE transitive_closure (source, target, weight, path_string) AS
  ( 
   --non-recurive term (as before)
   SELECT b.node_x, b.node_y, b.strength, b.node_x || '.' || b.node_y || '.' AS path_string
   FROM best_path b
   WHERE b.node_x = node_s --source_node

   UNION

   --recurive term
   SELECT tc.source, b.node_y, least(tc.weight,b.strength), tc.path_string || b.node_y || '.' AS path_string
   FROM best_path AS b JOIN transitive_closure AS tc ON b.node_x = tc.target
   WHERE tc.path_string NOT LIKE '%' || b.node_y || '.%'
     AND b.node_y = node_t 
     --will select only rows with direct edge to target

   UNION (second union is not allowed in CTE)

   SELECT those rows which do not have direct edge to target 
   AND which parents did not contribute to constructing the query above. 
   i.e. b.node_x = tc.target where there is no direct edge between b.node_x to node_t
  )

As a result of a query to find the paths from source node = 1, to target node = 4, I would like to have the following:

 source | target | strength | path_string
--------+--------+----------+------------
      1 |      2 |        1 | 1.2.
      1 |      3 |        1 | 1.3.
      1 |      4 |        1 | 1.2.4.
      1 |      6 |        1 | 1.3.6.
      1 |      7 |        1 | 1.3.7.

Thanks in advance for your help!

I have tried many ways already: e.g. conditions in FROM/WHERE clauses, trying to pass CTE into function, but no success.

Any suggestions will be appreciated.

I have my own recursive function that achieves what I want, however, it is very slow on enormous amount of data; and PostgreSQL's CTE apparently is well-optimized, so I would like to dig into it a bit more.

解决方案

You can make the searching of the path more efficient if you start at the bottom. Start at the children. If you start at the parent, it entails traversing all the children; whereas if you searched from the child, it has only one parent, hence won't waste time finding the path between source and target.

with recursive find_parent(source, target, recentness) as
(
    select source, target, 0 
    from tbl
    where target = 9

    union all

    select i.source, i.target, fp.recentness + 1
    from tbl i
    join find_parent fp on i.target = fp.source
),
construct_path(source, target, recentness, path) as
(
  select source, target, recentness, source || '.' || target
  from find_parent 
  where recentness = (select max(recentness) from find_parent)

  union

  select dd.source, dd.target, dd.recentness, cp.path || '.' || dd.target
  from find_parent dd
  join construct_path cp on dd.recentness = cp.recentness - 1  
)
select source, target, path 
from construct_path
order by recentness desc

Output:

SOURCE   TARGET   PATH
1        2        1.2
2        4        1.2.4
4        9        1.2.4.9

Live test: http://www.sqlfiddle.com/#!1/13e6b/1

Similar to this: How to get the parent given a child in SQL SERVER 2005


This is optimized, cut recursion to parent if it already find the specific one(source).

Source = 2

Target = 9

with recursive find_parent(source, target, recentness) as
(
    select source, target, 0 
    from tbl
    where target = 9

    union all

    select i.source, i.target, fp.recentness + 1
    from tbl i
    join find_parent fp on i.target = fp.source 
         -- despite the name, this target is another one's source
         and i.target <> 2
)
,construct_path(source, target, recentness, path) as
(
    select source, target, recentness, source || '.' || target
    from find_parent 
    where recentness = (select max(recentness) from find_parent)

    union

    select dd.source, dd.target, dd.recentness, cp.path || '.' || dd.target
    from find_parent dd
    join construct_path cp on dd.recentness = cp.recentness - 1  

)
select source, target, path
from construct_path
order by recentness desc

Output:

SOURCE   TARGET  PATH
2        4       2.4
4        9       2.4.9

Live test: http://www.sqlfiddle.com/#!1/13e6b/16

这篇关于PostgreSQL将数据从递归CTE传递到函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-01 08:22