【问题标题】:Parent/child table with include/exclude items包含/排除项目的父/子表
【发布时间】:2019-09-07 21:49:47
【问题描述】:

我有一张带有父子关系的表。关系可以深入到 n 级。 还有一个表格,其中包含属于某个组的元素。

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

因此,我们可以将此数据表示为 不一定是二叉树的形式,只是一个简单的例子。组可以包含 m 个孩子。

需要从右到左阅读。第 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;

SQL Fiddle

【问题讨论】:

    标签: postgresql recursion recursive-query


    【解决方案1】:

    demo:db<>fiddle

    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
    
    1. 非递归部分根据group_id 聚合所有名称
    2. 递归部分:加入孩子对抗父母
    3. 将名称数组扩展为每行一个元素。

    这会导致:

    | 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    |
    
    1. 现在您有了未嵌套的名称。现在你想找到那些必须被排除的。为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    |
    
    1. 之后,可以按parent_id 对未嵌套的名称进行分组。 FILTER 子句仅聚合 bool_andtrue 的元素。当然,您需要再次在窗口函数中执行此操作。这会创建可以被DISTINCT 子句删除的重复记录

    最终结果(当然可以通过元素1过滤):

    | group_id | array_agg |
    |----------|-----------|
    |        5 | {bbb,ccc} |
    |        4 | {aaa,bbb} |
    |        6 | {aaa,bbb} |
    |        7 | {aaa,ccc} |
    |        2 | {aaa}     |
    |        3 | {bbb}     |
    |        1 | {aaa}     |
    

    【讨论】:

      猜你喜欢
      • 2012-03-20
      • 1970-01-01
      • 1970-01-01
      • 2016-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-29
      • 1970-01-01
      相关资源
      最近更新 更多