sql - 在 group by 子句中连接数组

标签 sql arrays postgresql group-by postgresql-9.1

我们在将数组分组为单个数组时遇到了问题。 我们想将两列的值连接到一个数组中,然后聚合这些多行数组。

给定以下输入:

| id | name | col_1 | col_2 |
| 1  |  a   |   1   |   2   |
| 2  |  a   |   3   |   4   |
| 4  |  b   |   7   |   8   |
| 3  |  b   |   5   |   6   |

我们想要以下输出:

| a | { 1, 2, 3, 4 } |
| b | { 5, 6, 7, 8 } |

元素的顺序很重要,应该与聚合行的 ID 相关联。

我们尝试了 array_agg() 函数:

SELECT array_agg(ARRAY[col_1, col_2]) FROM mytable GROUP BY name;

不幸的是,这个语句引发了一个错误:

ERROR: could not find array type for data type character varying[]

似乎不可能使用 array_agg() 将数组合并到一个 group by 子句中。

有什么想法吗?

最佳答案

联合所有

您可以先使用 UNION ALL 进行“反转”:

SELECT name, array_agg(c) AS c_arr
FROM  (
   SELECT name, id, 1 AS rnk, col1 AS c FROM tbl
   UNION ALL
   SELECT name, id, 2, col2 FROM tbl
   ORDER  BY name, id, rnk
   ) sub
GROUP  BY 1;

适用于生成您稍后请求的值的顺序。 The manual:

The aggregate functions array_agg, json_agg, string_agg, and xmlagg, as well as similar user-defined aggregate functions, produce meaningfully different result values depending on the order of the input values. This ordering is unspecified by default, but can be controlled by writing an ORDER BY clause within the aggregate call, as shown in Section 4.2.7. Alternatively, supplying the input values from a sorted subquery will usually work.

大胆强调我的。

LATERAL subquery使用 VALUES 表达式

LATERAL 需要 Postgres 9.3 或更高版本。

SELECT t.name, array_agg(c) AS c_arr
FROM  (SELECT * FROM tbl ORDER BY name, id) t
CROSS  JOIN LATERAL (VALUES (t.col1), (t.col2)) v(c)
GROUP  BY 1;

同样的结果。只需要在 table 上走一遍。

自定义聚合函数

或者您可以创建自定义聚合函数,就像这些相关答案中讨论的那样:

CREATE AGGREGATE array_agg_mult (anyarray)  (
    SFUNC     = array_cat
  , STYPE     = anyarray
  , INITCOND  = '{}'
);

然后你可以:

SELECT name, array_agg_mult(ARRAY[col1, col2] ORDER BY id) AS c_arr
FROM   tbl
GROUP  BY 1
ORDER  BY 1;

或者,通常更快,但不是标准 SQL:

SELECT name, array_agg_mult(ARRAY[col1, col2]) AS c_arr
FROM  (SELECT * FROM tbl ORDER BY name, id) t
GROUP  BY 1;

添加的 ORDER BY id(可以附加到此类聚合函数)保证您想要的结果:

a | {1,2,3,4}
b | {5,6,7,8}

或者您可能对这个替代方案感兴趣:

SELECT name, array_agg_mult(ARRAY[ARRAY[col1, col2]] ORDER BY id) AS c_arr
FROM   tbl
GROUP  BY 1
ORDER  BY 1;

生成二维数组:

a | {{1,2},{3,4}}
b | {{5,6},{7,8}}

最后一个可以用 Postgres 9.5 或更高版本中的内置 array_agg() 替换(并且应该替换,因为它更快!) - 添加聚合数组的能力:

SELECT name, array_agg(ARRAY[col1, col2] ORDER BY id) AS c_arr
FROM   tbl
GROUP  BY 1
ORDER  BY 1;

同样的结果。 The manual:

input arrays concatenated into array of one higher dimension (inputs must all have same dimensionality, and cannot be empty or null)

所以和我们自定义的聚合函数array_agg_mult()不完全一样;

关于sql - 在 group by 子句中连接数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24557344/

相关文章:

java - 错误: No operations allowed after statement closed

javascript - 从多维数组中打印特定数组?

postgresql - psycopg2.OperationalError 与 openerp

ruby - 如何使用 sinatra/sequel 设置 application_name

postgresql - 无法使 log_min_duration_statement 起作用

sql - 检索扩展事件 session 环缓冲区中死锁的时间戳

sql - MySQL 使用 OR 执行多个查询

sql - 结合使用 string_agg 并在 postgres 中使用

java - 如何从字符串数组列表中随机选择

c - 将指向结构数组的指针设置为等于结构数组