【问题标题】:Aggregating all values not in the same group聚合不在同一组中的所有值
【发布时间】:2021-08-30 16:38:23
【问题描述】:

PostgreSQL中有没有办法取这个表:

ID country name values
1 USA John Smith {1,2,3}
2 USA Jane Smith {0,1,3}
3 USA Jane Doe {1,1,1}
4 USA John Doe {0,2,4}

并使用列 agg_values 从中生成此表:

ID country name values agg_values
1 USA John Smith {1,2,3} {0,1,3,1,1,1,0,2,4}
2 USA Jane Smith {0,1,3} {1,2,3,1,1,1,0,2,4}
3 USA Jane Doe {1,1,1} {1,2,3,0,1,3,0,2,4}
4 USA John Doe {0,2,4} {1,2,3,0,1,3,1,1,1}

每行聚合所有values,但当前行及其对等行除外。
所以如果name = John Smith 那么agg_values = aggregate of all values where name not = John Smith。这可能吗?

【问题讨论】:

    标签: sql postgresql aggregate-functions window-functions


    【解决方案1】:

    Postgres 11 或更高版本中,使用 window function with a custom frame and a frame_exclusion:

    SELECT *, array_combine(values) OVER (ROWS BETWEEN UNBOUNDED PRECEDING
                                               AND UNBOUNDED FOLLOWING
                                               EXCLUDE CURRENT ROW) AS agg_values
    FROM   tbl;
    

    如果name 不是UNIQUE,并且既然你问了:

    name 不为 John Smith 的所有值

    SELECT *, array_combine(values) OVER (ORDER BY name
                                          ROWS BETWEEN UNBOUNDED PRECEDING
                                               AND UNBOUNDED FOLLOWING
                                               EXCLUDE GROUP) AS agg_values
    FROM   tbl;
    

    db小提琴here

    第一个(也)适用于任意顺序的行,仅不包括当前行。第二个需要ORDER BY 来确定哪些行属于同一组。

    The manual:

    frame_exclusion 选项允许当前行周围的行 从框架中排除,即使它们将根据 帧开始和帧结束选项。 EXCLUDE CURRENT ROW 从框架中排除当前行。 EXCLUDE GROUP 不包括 框架中的当前行及其排序节点。 EXCLUDE TIES 从帧中排除当前行的任何对等点,但不排除 当前行本身。 [...]

    我的大胆强调。

    这使用自定义聚合函数array_combine(anyarray)provided by a_horse
    或者在这里:

    【讨论】:

    • 感谢您抽出宝贵时间回答。非常感谢!
    【解决方案2】:

    您可以对派生表使用横向连接,将名称不相等的所有行取消嵌套,然后将其聚合回数组中:

    select t1.*, xu.agg_values
    from the_table t1
      cross join lateral (
          select array_agg(tu.v) as agg_values
          from the_table t2
            cross join unnest(t2."values") as tu(v)
          where t2.name <> t1.name
      ) xu 
    

    这可以通过创建自定义聚合来简化,以避免取消嵌套和聚合:

    create aggregate array_combine(anyarray)
    (
      sfunc = array_cat(anyarray, anyarray),
      stype = anyarray
    );
    

    那么这可以写成:

    select t1.*, xu.agg_values
    from the_table t1
      cross join lateral (
          select array_combine(t2.values) as agg_values
          from the_table t2
          where t2.name <> t1.name
      ) xu 
    

    【讨论】:

    • 感谢您抽出宝贵时间回答。非常感谢!
    猜你喜欢
    • 2015-08-11
    • 1970-01-01
    • 2021-07-12
    • 2023-01-26
    • 2020-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多