2017-02-12 5 views
1

계층 구조 링크 (parent_id, child_id)의 그래프를 나타내는 테이블이 있습니다. 테이블에 부모, 자식 및 둘 모두에 대한 인덱스가 있습니다. 그래프에 루프가 포함되어있을 수 있으므로이를 확인해야합니다 (또는 루프를 제거하기 위해 루프를 찾아야 할 수도 있습니다).postgresql은 재귀 적 쿼리에 대한 인덱스를 무시합니다.

그리고 노드의 모든 부모를 재귀 적으로 쿼리해야합니다. 내가이 쿼리를 사용이를 위해 은 (보기에 저장하도록되어) :이 쿼리 포스트 그레스를 들어

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 

매우 훌륭한 계획을 사용하는 것입니다 :

"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)" 

이 포스트 그레스 같이하지 않는 것 같다 첫 번째 재귀 서브 쿼리에서 node_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 
     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

일반적으로 상위 노드 (부모 없음) 또는 리프 노드 (자식 없음) 또는 관심이있는 특정 레코드 # 조건이 UNION의 첫 번째 부분에 있습니다. 체인 스타터로서의 모든 기록. – wildplasser

+0

유니온의 첫 번째 쿼리는 테이블의 ** 모든 ** 행을 검색합니다. 인덱스가 도움이되지 않습니다. –

+0

postgres가 외부 필터를 내부 하위 쿼리와 병합 할 수 있기를 바랍니다. 나는 너무 낙관적 인 것처럼 보인다. – qMax

답변

1

보인다.

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$ 

자손의 경우에도 마찬가지입니다.

+0

그런 경우에는 조회를보기에 저장할 수 없으며 시작 필터에 대한 입력 매개 변수가있는 함수에만 저장하십시오. – qMax

+1

함수는 매개 변수화 된보기입니다 :-). 그리고 비 재귀 부분에서 쿼리 속도를 높일 수있는 경우에만 –

+0

예. 이 기능은 아마 내가 여기에 필요한 것일 것입니다. – qMax

0

작업을 통과 그래프를 해결하기 위해 훨씬 더 효과적인 방법은 다음과 같습니다 당신은 단지 노동 조합의 첫 번째 부분에 WHERE node_id = 883를 이동해야처럼