8

PostgreSQL에서 재귀 쿼리를 사용하여 전이 폐쇄를 설명하는 간단한 예제를 만들었습니다.전이 폐쇄에 사용되는 재귀 쿼리

그러나 내 재귀 쿼리를 사용하면 문제가 해결됩니다. 문법에 익숙하지 않아서이 요청이 전적으로 나에게 불쾌감을 줄 수 있습니다. 그 때문에 사전에 사과드립니다. 쿼리를 실행하면 노드 1이 경로 결과에서 반복되는 것을 볼 수 있습니다. 누군가 SQL을 조정하는 방법을 알아내는 데 도움을 줄 수 있습니까?

/*   1 
     / \ 
      2  3 
     /\ /
     4 5 6 
    /
     7 
    /\ 
    8 9 
*/ 

create table account(
acct_id INT, 
parent_id INT REFERENCES account(acct_id), 
acct_name VARCHAR(100), 
PRIMARY KEY(acct_id) 
); 

insert into account (acct_id, parent_id, acct_name) values (1,1,'account 1'); 
insert into account (acct_id, parent_id, acct_name) values (2,1,'account 2'); 
insert into account (acct_id, parent_id, acct_name) values (3,1,'account 3'); 
insert into account (acct_id, parent_id, acct_name) values (4,2,'account 4'); 
insert into account (acct_id, parent_id, acct_name) values (5,2,'account 5'); 
insert into account (acct_id, parent_id, acct_name) values (6,3,'account 6'); 
insert into account (acct_id, parent_id, acct_name) values (7,4,'account 7'); 
insert into account (acct_id, parent_id, acct_name) values (8,7,'account 8'); 
insert into account (acct_id, parent_id, acct_name) values (9,7,'account 9'); 

WITH RECURSIVE search_graph(acct_id, parent_id, depth, path, cycle) AS (
     SELECT g.acct_id, g.parent_id, 1, 
      ARRAY[g.acct_id], 
      false 
     FROM account g 
     UNION ALL 
     SELECT g.acct_id, g.parent_id, sg.depth + 1, 
      path || g.acct_id, 
      g.acct_id = ANY(path) 
     FROM account g, search_graph sg 
     WHERE g.acct_id = sg.parent_id AND NOT cycle 
) 
SELECT path[1] as Child,parent_id as Parent,path || parent_id as path FROM search_graph 
ORDER BY path[1],depth; 
+0

테스트 설정은 유용하고 유용하지만 결과가 정확히 같아야한다고 설명해주십시오. '이행 적 폐쇄'라는 핵심 단어를 던지면 설명이 충분하지 않습니다. –

답변

4

(acct_idparent_idNOT NULL입니다 가정) :

WITH RECURSIVE search_graph AS (
    SELECT parent_id, ARRAY[acct_id] AS path 
    FROM account 

    UNION ALL 
    SELECT g.parent_id, sg.path || g.acct_id 
    FROM search_graph sg 
    JOIN account g ON g.acct_id = sg.parent_id 
    WHERE g.acct_id <> ALL(sg.path) 
    ) 
SELECT path[1] AS child 
    , path[array_upper(path,1)] AS parent 
    , path 
FROM search_graph 
ORDER BY path; 
  • 열은 acct_id, depth, cycle는 쿼리에서 바로 소음이다.
  • WHERE 조건은 재귀를 한 단계 더 일찍 끝내야합니다. 보다 앞에이 나오면 맨 위 노드의 중복 항목이 결과에 나타납니다. 그것은 원래의 "오프 바이 한"것이 었습니다.

나머지는 서식을 지정합니다.

WITH RECURSIVE search_graph AS (
    SELECT parent_id, ARRAY[acct_id] AS path, acct_id <> parent_id AS keep_going 
    FROM account 

    UNION ALL 
    SELECT g.parent_id, sg.path || g.acct_id, g.acct_id <> g.parent_id 
    FROM search_graph sg 
    JOIN account g ON g.acct_id = sg.parent_id 
    WHERE sg.keep_going 
) 
SELECT path[1] AS child 
    , path[array_upper(path,1)] AS parent 
    , path 
FROM search_graph 
ORDER BY path; 

SQL Fiddle.

주 이상까지 문제 (이있을 것입니다 :

당신이 그래프에서 유일하게 가능한 원을 알고 경우가 자체 참조, 우리는 싼 것을 할 수 있습니다 (예 : varchar(5)) 배열 연결이 수정자를 잃었으므로 rCTE가 정확하게 일치하는 유형을 고집하기 때문에 :

+0

사려 깊은 답변을 주셔서 대단히 감사합니다! 두 쿼리 (심지어 후자)는 의도 한대로 정확하게 작동합니다. – Dowwie

0

계정 1은 자신의 부모로 설정되어 있습니다. 계정의 상위 항목을 null으로 설정하면 해당 계정을 시작 및 종료 노드로 사용하지 않아도됩니다. 논리를 설정하는 방법은주기를 포함하지만 그주기에 추가하지 않으므로 합리적입니다. 끝에 "null"이 없도록 최종 "경로"열을 case when parent_id is not null then path || parent_id else path end과 같이 변경하는 것이 조금 더 멋지게 보입니다. 당신은 여러 장소에서 간단하게 할 수

+0

샘플을 실행하고 확인 했습니까? 계정 1의 삽입을 null 상위로 변경하면 결과가 향상되지 않습니다. – Dowwie