多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;
我一直在努力优化纯粹在 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;