递归 SELECT,停止条件在 SQL?
Recursive SELECT with stop condition in SQL?
我的 table 称为 元素 看起来像这样:
id | successor | important
----------------------------
1 | NULL | 0
2 | 4 | 1
3 | 5 | 0
4 | 8 | 0
5 | 6 | 1
6 | 7 | 0
7 | NULL | 0
8 | 10 | 1
9 | 10 | 0
10 | NULL | 0
我从一个元素的 ID 开始。每个元素可能有也可能没有后续元素。因此,给定任何元素 ID,我可以从 0..n 个元素构建一个元素链,具体取决于它的后继者和后继者-后继者,依此类推。
假设我的起始 ID 是 2。这导致以下链:
2 -> 4 -> 8 -> 10
现在我想问这个问题:一个特定的元素链是否包含至少一个重要== 1的元素?
在伪代码中,无需不必要的检查即可实现此功能的函数可能如下所示:
boolean chainIsImportant(element)
{
if (element.important == 1) {
return true;
}
if (element.successor != NULL) {
return chainIsImportant(element.successor);
}
return false;
}
我想这可以用WITH RECURSIVE
实现,对吧?一旦找到具有 important == 1 的元素,如何停止递归?
这通常是通过聚合有问题的列并在 CTE 的递归部分中添加连接条件来完成的:
with recursive all_elements as (
select id, successor, important, array[important] as important_elements
from elements
where successor is null
union all
select c.id, c.successor, c.important, p.important_elements||c.important
from elements c
join all_elements p on c.successor = p.id
where 1 <> all(p.important_elements)
)
select *
from all_elements;
请注意,条件是 "flipped",因为 where 子句定义了那些应该包含的行。
CREATE TABLE booltree
( id INTEGER NOT NULL PRIMARY KEY
, successor INTEGER REFERENCES booltree(id)
, important Boolean NOT NULL
);
INSERT INTO booltree(id , successor , important) VALUES
( 1, NULL , False)
,(2, 4 , True)
,(3, 5 , False)
,(4, 8 , False)
,(5, 6 , True)
,(6, 7 , False)
,(7, NULL , False)
,(8, 10 , True)
,(9, 10 , False)
,(10, NULL , False)
;
-- SELECT * FROM booltree;
WITH RECURSIVE rec AS (
SELECT id, important
FROM booltree
WHERE successor IS NULL
UNION ALL
SELECT bt.id, GREATEST(rec.important, bt.important) AS important
FROM booltree bt
JOIN rec ON bt.successor = rec.id
)
SELECT id, important
FROM rec
ORDER BY important, id;
结果:
CREATE TABLE
INSERT 0 10
id | important
----+-----------
1 | f
6 | f
7 | f
9 | f
10 | f
2 | t
3 | t
4 | t
5 | t
8 | t
(10 rows)
注意:恕我直言,一旦发现真正的重要性就无法停止递归(基本上,因为在递归联合中不允许使用左连接)
但是,如果您正在寻找一个给定的 id(或一组),那么也许您可以将其用作开始条件,然后向上搜索树。
我的 table 称为 元素 看起来像这样:
id | successor | important
----------------------------
1 | NULL | 0
2 | 4 | 1
3 | 5 | 0
4 | 8 | 0
5 | 6 | 1
6 | 7 | 0
7 | NULL | 0
8 | 10 | 1
9 | 10 | 0
10 | NULL | 0
我从一个元素的 ID 开始。每个元素可能有也可能没有后续元素。因此,给定任何元素 ID,我可以从 0..n 个元素构建一个元素链,具体取决于它的后继者和后继者-后继者,依此类推。
假设我的起始 ID 是 2。这导致以下链:
2 -> 4 -> 8 -> 10
现在我想问这个问题:一个特定的元素链是否包含至少一个重要== 1的元素?
在伪代码中,无需不必要的检查即可实现此功能的函数可能如下所示:
boolean chainIsImportant(element)
{
if (element.important == 1) {
return true;
}
if (element.successor != NULL) {
return chainIsImportant(element.successor);
}
return false;
}
我想这可以用WITH RECURSIVE
实现,对吧?一旦找到具有 important == 1 的元素,如何停止递归?
这通常是通过聚合有问题的列并在 CTE 的递归部分中添加连接条件来完成的:
with recursive all_elements as (
select id, successor, important, array[important] as important_elements
from elements
where successor is null
union all
select c.id, c.successor, c.important, p.important_elements||c.important
from elements c
join all_elements p on c.successor = p.id
where 1 <> all(p.important_elements)
)
select *
from all_elements;
请注意,条件是 "flipped",因为 where 子句定义了那些应该包含的行。
CREATE TABLE booltree
( id INTEGER NOT NULL PRIMARY KEY
, successor INTEGER REFERENCES booltree(id)
, important Boolean NOT NULL
);
INSERT INTO booltree(id , successor , important) VALUES
( 1, NULL , False)
,(2, 4 , True)
,(3, 5 , False)
,(4, 8 , False)
,(5, 6 , True)
,(6, 7 , False)
,(7, NULL , False)
,(8, 10 , True)
,(9, 10 , False)
,(10, NULL , False)
;
-- SELECT * FROM booltree;
WITH RECURSIVE rec AS (
SELECT id, important
FROM booltree
WHERE successor IS NULL
UNION ALL
SELECT bt.id, GREATEST(rec.important, bt.important) AS important
FROM booltree bt
JOIN rec ON bt.successor = rec.id
)
SELECT id, important
FROM rec
ORDER BY important, id;
结果:
CREATE TABLE
INSERT 0 10
id | important
----+-----------
1 | f
6 | f
7 | f
9 | f
10 | f
2 | t
3 | t
4 | t
5 | t
8 | t
(10 rows)
注意:恕我直言,一旦发现真正的重要性就无法停止递归(基本上,因为在递归联合中不允许使用左连接) 但是,如果您正在寻找一个给定的 id(或一组),那么也许您可以将其用作开始条件,然后向上搜索树。