Parent/child table 有 include/exclude 项
Parent/child table with include/exclude items
我有 table 和 parent-child 关系。关系可以深入n-level。
还有一个 table 包含属于一个组的元素。
CREATE TABLE group_children(
id serial PRIMARY KEY,
parent_id integer,
children_id integer,
contains boolean
);
CREATE TABLE group_item(
id serial PRIMARY KEY,
group_id integer,
name text
);
INSERT INTO group_children(parent_id, children_id, contains) VALUES
(1, 2, true),
(1, 3, false),
(2, 4, true),
(2, 5, false),
(3, 6, true),
(3, 7, false);
INSERT INTO group_item(group_id, name) VALUES
(4, 'aaa'),
(4, 'bbb'),
(5, 'bbb'),
(5, 'ccc'),
(6, 'aaa'),
(6, 'bbb'),
(7, 'aaa'),
(7, 'ccc');
因此,我们可以将此数据表示为
不一定是二叉树的形式,简单的case即可。组可以包含 m child.
需要从右到左阅读。第 4 组包含 ['aaa'、'bbb']、第 5 组 - ['bbb'、'ccc']。第 2 组包括第 4 组中的所有项目并从第 5 组中排除。因此第 2 组包含 ['aaa']。等等。毕竟计算组 1 将包含 ['aaa'].
问题是:如何构建 sql 查询以获取属于组 1 的所有项目?
我能做的:
WITH RECURSIVE r AS (
SELECT group_children.parent_id, group_children.children_id, group_children.contains, group_item.name
FROM group_children
LEFT JOIN group_item ON group_children.children_id = group_item.group_id
WHERE parent_id = 1
UNION ALL
SELECT group_children.parent_id, group_children.children_id, group_children.contains, group_item.name
FROM group_children
LEFT JOIN group_item ON group_children.children_id = group_item.group_id
JOIN r ON group_children.parent_id = r.children_id
)
SELECT * FROM r;
WITH RECURSIVE items AS (
SELECT -- 1
group_id,
array_agg(name)
FROM
group_Item
GROUP BY group_id
UNION
SELECT DISTINCT
parent_id,
array_agg(unnest) FILTER (WHERE bool_and) OVER (PARTITION BY parent_id) -- 5
FROM (
SELECT
parent_id,
unnest,
bool_and(contains) OVER (PARTITION BY parent_id, unnest) -- 4
FROM items i
JOIN group_children gc -- 2
ON i.group_id = gc.children_id,
unnest(array_agg) -- 3
) s
)
SELECT * FROM items
- non-recursive 部分汇总了每个
group_id
的所有名称
- 递归部分:加入孩子对抗他们的 parents
- 将名称数组扩展为每行一个元素。
这导致:
| group_id | array_agg | id | parent_id | children_id | contains | unnest |
|----------|-----------|----|-----------|-------------|----------|--------|
| 4 | {aaa,bbb} | 3 | 2 | 4 | true | aaa |
| 4 | {aaa,bbb} | 3 | 2 | 4 | true | bbb |
| 5 | {bbb,ccc} | 4 | 2 | 5 | false | bbb |
| 5 | {bbb,ccc} | 4 | 2 | 5 | false | ccc |
| 6 | {aaa,bbb} | 5 | 3 | 6 | true | aaa |
| 6 | {aaa,bbb} | 5 | 3 | 6 | true | bbb |
| 7 | {aaa,ccc} | 6 | 3 | 7 | false | aaa |
| 7 | {aaa,ccc} | 6 | 3 | 7 | false | ccc |
- 现在您有了未嵌套的名称。现在您想找到必须排除的那些。以
parent_id = 2
的 bbb 元素为例: contains = true
一行,contains = false
一行。这应该被排除在外。因此,每个 parent_id
的所有名称都必须分组。包含的值可以使用布尔运算符进行聚合。如果所有元素都是 true
,聚合函数 bool_and
只给出 true
。所以 bbb
会得到一个 false
(聚合需要作为 window function 完成,因为由于某些原因在递归部分中不允许 GROUP BY
):
结果:
| parent_id | unnest | bool_and |
|-----------|--------|----------|
| 2 | aaa | true |
| 2 | bbb | false |
| 2 | bbb | false |
| 2 | ccc | false |
| 3 | aaa | false |
| 3 | aaa | false |
| 3 | bbb | true |
| 3 | ccc | false |
- 之后,未嵌套的名称可以按照
parent_id
进行分组。 FILTER
子句仅聚合 bool_and
为 true
的元素。当然,您需要在 window 函数中再次执行此操作。这会创建重复的记录,可以通过 DISTINCT
子句 删除
最终结果(当然可以通过元素1
过滤):
| group_id | array_agg |
|----------|-----------|
| 5 | {bbb,ccc} |
| 4 | {aaa,bbb} |
| 6 | {aaa,bbb} |
| 7 | {aaa,ccc} |
| 2 | {aaa} |
| 3 | {bbb} |
| 1 | {aaa} |
我有 table 和 parent-child 关系。关系可以深入n-level。 还有一个 table 包含属于一个组的元素。
CREATE TABLE group_children(
id serial PRIMARY KEY,
parent_id integer,
children_id integer,
contains boolean
);
CREATE TABLE group_item(
id serial PRIMARY KEY,
group_id integer,
name text
);
INSERT INTO group_children(parent_id, children_id, contains) VALUES
(1, 2, true),
(1, 3, false),
(2, 4, true),
(2, 5, false),
(3, 6, true),
(3, 7, false);
INSERT INTO group_item(group_id, name) VALUES
(4, 'aaa'),
(4, 'bbb'),
(5, 'bbb'),
(5, 'ccc'),
(6, 'aaa'),
(6, 'bbb'),
(7, 'aaa'),
(7, 'ccc');
因此,我们可以将此数据表示为
需要从右到左阅读。第 4 组包含 ['aaa'、'bbb']、第 5 组 - ['bbb'、'ccc']。第 2 组包括第 4 组中的所有项目并从第 5 组中排除。因此第 2 组包含 ['aaa']。等等。毕竟计算组 1 将包含 ['aaa'].
问题是:如何构建 sql 查询以获取属于组 1 的所有项目?
我能做的:
WITH RECURSIVE r AS (
SELECT group_children.parent_id, group_children.children_id, group_children.contains, group_item.name
FROM group_children
LEFT JOIN group_item ON group_children.children_id = group_item.group_id
WHERE parent_id = 1
UNION ALL
SELECT group_children.parent_id, group_children.children_id, group_children.contains, group_item.name
FROM group_children
LEFT JOIN group_item ON group_children.children_id = group_item.group_id
JOIN r ON group_children.parent_id = r.children_id
)
SELECT * FROM r;
WITH RECURSIVE items AS (
SELECT -- 1
group_id,
array_agg(name)
FROM
group_Item
GROUP BY group_id
UNION
SELECT DISTINCT
parent_id,
array_agg(unnest) FILTER (WHERE bool_and) OVER (PARTITION BY parent_id) -- 5
FROM (
SELECT
parent_id,
unnest,
bool_and(contains) OVER (PARTITION BY parent_id, unnest) -- 4
FROM items i
JOIN group_children gc -- 2
ON i.group_id = gc.children_id,
unnest(array_agg) -- 3
) s
)
SELECT * FROM items
- non-recursive 部分汇总了每个
group_id
的所有名称
- 递归部分:加入孩子对抗他们的 parents
- 将名称数组扩展为每行一个元素。
这导致:
| group_id | array_agg | id | parent_id | children_id | contains | unnest |
|----------|-----------|----|-----------|-------------|----------|--------|
| 4 | {aaa,bbb} | 3 | 2 | 4 | true | aaa |
| 4 | {aaa,bbb} | 3 | 2 | 4 | true | bbb |
| 5 | {bbb,ccc} | 4 | 2 | 5 | false | bbb |
| 5 | {bbb,ccc} | 4 | 2 | 5 | false | ccc |
| 6 | {aaa,bbb} | 5 | 3 | 6 | true | aaa |
| 6 | {aaa,bbb} | 5 | 3 | 6 | true | bbb |
| 7 | {aaa,ccc} | 6 | 3 | 7 | false | aaa |
| 7 | {aaa,ccc} | 6 | 3 | 7 | false | ccc |
- 现在您有了未嵌套的名称。现在您想找到必须排除的那些。以
parent_id = 2
的 bbb 元素为例:contains = true
一行,contains = false
一行。这应该被排除在外。因此,每个parent_id
的所有名称都必须分组。包含的值可以使用布尔运算符进行聚合。如果所有元素都是true
,聚合函数bool_and
只给出true
。所以bbb
会得到一个false
(聚合需要作为 window function 完成,因为由于某些原因在递归部分中不允许GROUP BY
):
结果:
| parent_id | unnest | bool_and |
|-----------|--------|----------|
| 2 | aaa | true |
| 2 | bbb | false |
| 2 | bbb | false |
| 2 | ccc | false |
| 3 | aaa | false |
| 3 | aaa | false |
| 3 | bbb | true |
| 3 | ccc | false |
- 之后,未嵌套的名称可以按照
parent_id
进行分组。FILTER
子句仅聚合bool_and
为true
的元素。当然,您需要在 window 函数中再次执行此操作。这会创建重复的记录,可以通过DISTINCT
子句 删除
最终结果(当然可以通过元素1
过滤):
| group_id | array_agg |
|----------|-----------|
| 5 | {bbb,ccc} |
| 4 | {aaa,bbb} |
| 6 | {aaa,bbb} |
| 7 | {aaa,ccc} |
| 2 | {aaa} |
| 3 | {bbb} |
| 1 | {aaa} |