postgresql忽略递归查询的索引

时间:2017-02-12 17:15:45

标签: postgresql hierarchical-data recursive-query

我有一个表格,表示层次结构链接图(parent_id,child_id) 该表包含父,子和两者的索引。 图表可能包含循环,我需要检查它们(或者,我可能需要找到所有循环以消除它们)。

我需要重新查询节点的所有父节点。 为此,我使用此查询(它应该保存在视图中):

WITH RECURSIVE recursion(parent_id, child_id, node_id, path) AS (
     SELECT h.parent_id,
        h.child_id,
        h.child_id AS node_id,
        ARRAY[h.parent_id, h.child_id] AS path
       FROM hierarchy h
    UNION ALL
     SELECT h.parent_id,
        h.child_id,
        r.node_id,
        ARRAY[h.parent_id] || r.path 
       FROM hierarchy h JOIN recursion r ON h.child_id = r.parent_id
      WHERE NOT r.path @> ARRAY[h.parent_id]
    )
 SELECT parent_id,
    child_id,
    node_id,
    path
   FROM recursion
   where node_id = 883

对于这个查询,postgres将使用非常棒的计划:

"CTE Scan on recursion  (cost=2703799682.88..4162807558.70 rows=324223972 width=56)"
"  Filter: (node_id = 883)"
"  CTE recursion"
"    ->  Recursive Union  (cost=0.00..2703799682.88 rows=64844794481 width=56)"
"          ->  Seq Scan on hierarchy h  (cost=0.00..74728.61 rows=4210061 width=56)"
"          ->  Merge Join  (cost=10058756.99..140682906.47 rows=6484058442 width=56)"
"                Merge Cond: (h_1.child_id = r.parent_id)"
"                Join Filter: (NOT (r.path @> ARRAY[h_1.parent_id]))"
"                ->  Index Scan using hierarchy_idx_child on hierarchy h_1  (cost=0.43..256998.25 rows=4210061 width=16)"
"                ->  Materialize  (cost=10058756.56..10269259.61 rows=42100610 width=48)"
"                      ->  Sort  (cost=10058756.56..10164008.08 rows=42100610 width=48)"
"                            Sort Key: r.parent_id"
"                            ->  WorkTable Scan on recursion r  (cost=0.00..842012.20 rows=42100610 width=48)"

似乎postgres不理解node_id上​​的外部过滤器在第一个递归子查询中应用于child_id。

我想我做错了。但到底在哪里?

2 个答案:

答案 0 :(得分:1)

看起来你只需要将WHERE node_id = 883移到联合的第一部分:

WITH RECURSIVE recursion(parent_id, child_id, node_id, path) AS (
     SELECT h.parent_id,
        h.child_id,
        h.child_id AS node_id,
        ARRAY[h.parent_id, h.child_id] AS path
       FROM hierarchy h
      WHERE node_id = 883
    UNION ALL
     SELECT h.parent_id,
        h.child_id,
        r.node_id,
        ARRAY[h.parent_id] || r.path 
       FROM hierarchy h JOIN recursion r ON h.child_id = r.parent_id
      WHERE NOT r.path @> ARRAY[h.parent_id]
    )
 SELECT parent_id,
    child_id,
    node_id,
    path
   FROM recursion

答案 1 :(得分:0)

这是解决图遍历任务的更有效方法。

CREATE OR REPLACE FUNCTION public.terr_ancestors(IN bigint)
RETURNS TABLE(node_id bigint, depth integer, path bigint[]) AS
$BODY$
WITH RECURSIVE recursion(node_id, depth, path) AS (
  SELECT $1 as node_id, 0, ARRAY[$1] AS path
  UNION ALL
  SELECT h.parent_id as node_id, r.depth + 1, h.parent_id || r.path
    FROM hierarchy h JOIN recursion r ON h.child_id = r.node_id
    WHERE h.parent_id != ANY(path)
)
SELECT * FROM recursion
$BODY$

对于后代也是如此。