0

전 순수하게 루비에서 수행되는 재귀 호출을 최적화하기 위해 고심하고 있습니다. 나는 데이터를 postgresql 데이터베이스로 옮겼고, postgresql이 제공하는 WITH RECURSIVE 함수를 사용하고 싶습니다.다중 테이블 재귀 sql 문

모든 예제에서 메뉴 또는 카테고리 테이블과 같은 단일 테이블을 사용하는 것 같습니다.

내 상황이 약간 다릅니다. 나는 질문과 대답 테이블을 가지고있다.

+----------------------+  +------------------+ 
| questions   |  | answers   | 
+----------------------+  +------------------+ 
| id     |  | source_id  | <- from question ID 
| start_node (boolean) |  | target_id  | <- to question ID 
| end_node (boolean) |  +------------------+ 
+----------------------+ 

관련 질문으로 모두 연결된 질문을 가져오고 싶습니다.

트리에서 다른 노드로 이동할 수도 있습니다. 예를 들어 주어진 노드에서 트리의 루트 노드까지 갈 수 있습니다.

그래픽 방식으로 질문 - 답변 나무의 또 다른 예를 제공하려면 : 당신이 볼 수 있듯이

Q1 
|-- A1 
| '-- Q2 
|   |-- A2 
|   | '-- Q3 
|   '-- A3 
|    '-- Q4 
'-- A4 
     '-- Q5 

이 질문은 여러 나가는 질문이있을 수 있지만, 그들은 또한 여러 들어오는 답변을 할 수 있습니다 - 어떤 - 투 - 많은.

누군가가 좋은 아이디어가 있거나, 몇 가지 예, 기사 또는 가이드를 알려주기를 바랍니다.

미리 감사드립니다.

감사합니다, 에밀

+0

이것은 재귀 적으로 보이지 않습니다. 질문이 하나의 질문에 대한 답일 수 있습니까? (메타 질문, 이건 ...) – wildplasser

+0

대답은 질문에 대한 대답이지만 대답은 다른 질문으로 이어집니다. 그래서 사슬은 무한히 길 수 있습니다. 대답은 체인의 초기 단계에서 통과 한 질문으로 되돌아 갈 수 있습니다. 이 질문에 대한 답변을 희망하십니까? –

답변

1

이 훨씬 이상적에서, 멀리 있지만, 조인을 통해 내가 그와 같은 재귀 쿼리 놀러 것이다 : 제공

WITH RECURSIVE questions_with_answers AS (
    SELECT 
     q.*, a.* 
    FROM 
     questions q 
    LEFT OUTER JOIN 
     answers a ON (q.id = a.source_id) 

    UNION ALL 

    SELECT 
     q.*, a.* 
    FROM 
     questions_with_answers qa 
    JOIN 
     questions q ON (qa.target_id = q.id) 
    LEFT OUTER JOIN 
     answers a ON (q.id = a.source_id) 
) 
SELECT * FROM questions_with_answers WHERE source_id IS NOT NULL AND target_id IS NOT NULL; 

나를 결과 :

id | name | start_node | end_node | source_id | target_id 
----+------+------------+----------+-----------+----------- 
    1 | Q1 |   |   |   1 |   2 
    2 | A1 |   |   |   2 |   3 
    3 | Q2 |   |   |   3 |   4 
    3 | Q2 |   |   |   3 |   6 
    4 | A2 |   |   |   4 |   5 
    6 | A3 |   |   |   6 |   7 
    1 | Q1 |   |   |   1 |   8 
    8 | A4 |   |   |   8 |   9 
    2 | A1 |   |   |   2 |   3 
    3 | Q2 |   |   |   3 |   6 
    3 | Q2 |   |   |   3 |   4 
    4 | A2 |   |   |   4 |   5 
    6 | A3 |   |   |   6 |   7 
    8 | A4 |   |   |   8 |   9 
    3 | Q2 |   |   |   3 |   6 
    3 | Q2 |   |   |   3 |   4 
    6 | A3 |   |   |   6 |   7 
    4 | A2 |   |   |   4 |   5 
    6 | A3 |   |   |   6 |   7 
    4 | A2 |   |   |   4 |   5 
(20 rows) 
+0

안녕하세요. 이 답변에 대해 고마워요 (!). 정말 대단했습니다! 약간 조정 해 보았습니다 만 체인에서 마지막 질문을 결과 테이블에 포함시킬 수 없습니다. 여기에 요점을 만들었습니다 : https://gist.github.com/ekampp/cb871918adc0fc931cb02afa20c30651 –

+0

@EmilKampp, source_id와 target_id에 대해 'NOT NULL'조건으로 인해 필터링 된 것 같습니다. 'source_id가 NULL이 아니거나 target_id가 NULL이 아닌 곳으로 변경하려고 시도하십시오. ' – icuken

+0

네, 그게 내가 도착한 곳이기도합니다. 도와 줘서 고마워! : D –

1

실제로 두 테이블이 필요하지 않습니다. 이 예제를 분석해 보시기 바랍니다. 2 개가 아닌 하나의 테이블을 유지하면 재귀 쿼리와 관련하여 많은 문제를 줄일 수 있습니다.

이 최소한의 구조

필요한 모든 정보를 포함 노드가 질문은

create table the_table (id int primary key, parent_id int); 
insert into the_table values 
(1, 0), -- root question 
(2, 1), 
(3, 1), 
(4, 2), 
(5, 2), 
(6, 1), 
(7, 3), 
(8, 0), -- root question 
(9, 8); 

여부를하거나 대답은 트리에서의 위치에 따라 달라집니다. 물론 노드 유형에 대한 정보가있는 열을 테이블에 추가 할 수 있습니다.

사용이 쿼리는 모두 사용자의 요청 (주석 적절한 where 조건)에 대한 답변을 얻을 수 있습니다 :

with recursive cte(id, parent_id, depth, type, root) as (
    select id, parent_id, 1, 'Q', id 
    from the_table 
    where parent_id = 0 
    -- and id = 1 <-- looking for list of a&q for root question #1 
union all 
    select 
     t.id, t.parent_id, depth+ 1, 
     case when (depth & 1)::boolean then 'A' else 'Q' end, c.root 
    from cte c 
    join the_table t on t.parent_id = c.id 
) 
select * 
from cte 
-- where id = 9 <-- looking for root question for answer #9 
order by id; 

id | parent_id | depth | type | root 
----+-----------+-------+------+------ 
    1 |   0 |  1 | Q | 1 
    2 |   1 |  2 | A | 1 
    3 |   1 |  2 | A | 1 
    4 |   2 |  3 | Q | 1 
    5 |   2 |  3 | Q | 1 
    6 |   1 |  2 | A | 1 
    7 |   3 |  3 | Q | 1 
    8 |   0 |  1 | Q | 8 
    9 |   8 |  2 | A | 8 
(9 rows) 

관계 아이 - 부모가 모호하고 양쪽에 적용됩니다. 이 정보를 두 번 저장할 필요가 없습니다. 즉, 부모에 대한 정보를 저장하면 자녀에 대한 정보가 중복됩니다 (반대의 경우도 마찬가지 임). tree이라는 데이터 구조의 기본 속성 중 하나입니다. 예를 참조하십시오 :

-- find parent of node #6 
select parent_id 
from the_table 
where id = 6; 

-- find children of node #6 
select id 
from the_table 
where parent_id = 6; 
+0

답변에 두 가지 테이블이 필요하다고 생각합니까? 대답에는 그렇지 않은 질문에 대한 연결에 대한 정보가 포함되어 있으니까요? –

+0

수정 된 답변보기 – klin