多table递归sql语句

Multi-table recursive sql statement

我一直在努力优化纯粹在 ruby 中完成的递归调用。我已将数据移动到 postgresql 数据库中,我想利用 postgresql 提供的 WITH RECURSIVE 函数。

我能找到的所有示例似乎都使用单个 table,例如菜单或类别 table。

我的情况略有不同。我有问题和答案 table。

+----------------------+     +------------------+
| 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

如您所见,一个问题可以有多个传出问题,但它们也可以有多个传入答案 -- 任意对多。

我希望有人有好主意,或者可以给我一些示例、文章或指南。

先谢谢大家了。

此致, 埃米尔

这远非理想,但我会尝试通过连接进行递归查询,例如:

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)

事实上,您不需要两个 table。 我想鼓励你分析这个例子。 维护一个 table 而不是两个将为您省去很多麻烦,尤其是在涉及递归查询时。

这个最小结构包含所有必要的信息:

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

节点是问题还是答案取决于它在树中的位置。当然,您可以在 table 中添加一个包含节点类型信息的列。

使用此查询获取您的两个请求的答案(取消注释足够的 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)

关系 child - parent 是明确的,适用于双方。无需将此信息存储两次。换句话说,如果我们存储有关 parents 的信息,则有关 children 的信息是多余的(反之亦然)。它是称为 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;