postgres中的递归路径查找

时间:2016-07-18 14:19:03

标签: sql postgresql hierarchical-data recursive-query

我遇到了Postgres 9.3+的一个问题而且我不知何故被卡住了。我有以下结构:

任务是将特定对象翻译成另一个对象(基本上是回答像#34这样的问题;谁将此发票归属于?")。

对象由ID标识,可能的翻译存储在如下表中:

vagrant=# select * from sys_context_translation;
 source_context | target_context | sys_service_id 
----------------+----------------+----------------
              1 |              2 |              1
              3 |              2 |              2
              2 |              1 |              1
              1 |              4 |              1
              4 |              5 |              2
              4 |              2 |              3
(6 rows)

如你所见,有一条从3到5的路径,如3 - 2 - 1 - 4 - 5.

我现在需要一个向我显示该路径的查询。 (因此,source_context 3为1行,下一个为2,下一个为1,依此类推......)。我现在有以下查询,但它没有返回所需的结果:

WITH RECURSIVE translation (source_context, target_context, sys_service_id)
AS
(
  SELECT
    source_context,
    target_context,
    sys_service_id
  FROM sys_context_translation AS s1
  WHERE source_context = 3
  UNION ALL
  SELECT
    s2.source_context,
    s2.target_context,
    s2.sys_service_id
  FROM sys_context_translation AS s2, translation AS t1
  WHERE t1.target_context = s2.source_context
) SELECT *
  FROM translation

;

它确实返回很多,但随后不断返回行。以下示例限制为10行。

 source_context | target_context | sys_service_id 
----------------+----------------+----------------
              3 |              2 |              2 (good one)
              2 |              1 |              1 (good one)
              1 |              2 |              1 (not useful)
              1 |              4 |              1 (good one)
              2 |              1 |              1 (not useful)
              4 |              5 |              2 (good one)
              4 |              2 |              3 (should have stopped a row before)
              1 |              2 |              1 (...)
              2 |              1 |              1
              1 |              4 |              1
(10 rows)

我非常感谢任何提示。

1 个答案:

答案 0 :(得分:1)

您的数据中存在循环(循环依赖关系),因此您的查询无穷无尽。 查询应检查找到的路径是否不包含重复。 您可以通过使用路径数组来实现此目的。 (我跳过sys_service_id无关紧要)。

with recursive translation (source_context, target_context, path)
as
(
    select
        source_context,
        target_context,
        array[source_context, target_context]
    from sys_context_translation
    where source_context = 3
union all
    select
        s.source_context,
        s.target_context,
        path || s.target_context
    from sys_context_translation as s
    join translation as t
    on t.target_context = s.source_context
    and s.target_context <> all(t.path)
) 
select *
from translation;

 source_context | target_context |    path     
----------------+----------------+-------------
              3 |              2 | {3,2}
              2 |              1 | {3,2,1}
              1 |              4 | {3,2,1,4}
              4 |              5 | {3,2,1,4,5}
(4 rows)