聚合不在同一组中的所有值

Aggregating all values not in the same group

在 PostgreSQL 中有没有办法接受这个 table:

ID 国家 姓名
1 美国 约翰·史密斯 {1,2,3}
2 美国 简·史密斯 {0,1,3}
3 美国 李四 {1,1,1}
4 美国 李四 {0,2,4}

并使用列 agg_values:

从中生成此 table
ID 国家 姓名 agg_values
1 美国 约翰·史密斯 {1,2,3} {0,1,3,1,1,1,0,2,4}
2 美国 简·史密斯 {0,1,3} {1,2,3,1,1,1,0,2,4}
3 美国 李四 {1,1,1} {1,2,3,0,1,3,0,2,4}
4 美国 李四 {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。这可能吗?

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

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 

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,既然你问了:

all values where name not = 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<>fiddle here

第一个(也)适用于任意顺序的行,只排除当前行。第二个要求 ORDER BY 确定哪些行在同一组中。

The manual:

The frame_exclusion option allows rows around the current row to be excluded from the frame, even if they would be included according to the frame start and frame end options. EXCLUDE CURRENT ROW excludes the current row from the frame. EXCLUDE GROUP excludes the current row and its ordering peers from the frame. EXCLUDE TIES excludes any peers of the current row from the frame, but not the current row itself. [...]

大胆强调我的。

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

  • Selecting data into a Postgres array
  • Is there something like a zip() function in PostgreSQL that combines two arrays?