array_agg(), string_agg(), jsonb_agg(), jsonb_object_agg()

The aggregate functions array_agg(), string_agg(), jsonb_agg(), and jsonb_object_agg() are described in this same section because each produces, as a single value, a list of the values that are aggregated. (The term "list" is used informally and somewhat loosely here. The examples make the sense in which the word is used clear.)

The notion "list" implies that the list members are ordered. (In contrast, "set" brings no such ordering notion.) For array_agg(), string_agg(), jsonb_agg(), and json_agg(), the list order is determined by the ORDER BY clause in the SELECT list expression that invokes the aggregate function. Because the single value produced by jsonb_object_agg() (and json_object_agg()) is a JSON object, and because the elements of such a value have no order, the ORDER BY clause in the SELECT list expression has no effect. (The elements are accessed by field name.)

This makes array_agg(), string_agg(), and jsonb_agg() unique among ordinary aggregate functions. Usually, as the example of sum() demonstrates, the order of the aggregated values is of no consequence. (The term "ordinary" aggregate function is used for the ones that are invoked with the GROUP BY syntax or the OVER syntax. The formal terms "within-group ordered-set" aggregate function and "within-group hypothetical-set" aggregate function are used for the "extraordinary" kinds.)

array_agg()

Signature:

input value:       anynonarray
                   anyarray

return value:      anyarray

Purpose: Returns an array whose elements are the individual values that are aggregated. The to-be-aggregated values must be homogeneous: either all nonarray values; or all array values with the same dimensionality, as returned by array_ndims(), as each other. See also the account of array_agg() in the dedicated Array data types and functionality section.

The order of the resulting array elements (i.e. the mapping of element value to index value) is determined by the order defined by the GROUP BY or the OVER invocation syntax. When nonarray (i.e. scalar) values are aggregated, the result is a 1-dimensional array. When anyarray values with dimensionality N are aggregated, the result is an array with dimensionality (N + 1).

string_agg()

Signature:

input value:       text, text
                   bytea, bytea

return value:      text
                   bytea

Purpose: Returns a single value produced by concatenating the aggregated values (first argument) separated by a mandatory separator (second argument). The first overload has text inputs and returns text. The second overload has bytea inputs and returns bytea. (The PostgreSQL documentation describes the bytea data type in section 8.4. Binary Data Types.

Here's a basic example:

drop table if exists t cascade;
create table t(k int primary key, vt text not null, vb bytea not null);
insert into t(k, vt, vb) values
  (1, 'm', 'm'::bytea),
  (2, 'o', 'o'::bytea),
  (3, 'u', 'u'::bytea),
  (4, 's', 's'::bytea),
  (5, 'e', 'e'::bytea);

select vt, vb from t order by k;

This is the result:

 vt |  vb
----+------
 m  | \x6d
 o  | \x6f
 u  | \x75
 s  | \x73
 e  | \x65

Now try this:

with a as (
  select
    string_agg(vt, null       order by k) as text_agg,
    string_agg(vb, '.'::bytea order by k) as bytea_agg
  from t)
select
  text_agg,
  bytea_agg,
  convert_from(bytea_agg, 'utf-8') as bytea_agg_text
from a;

This is the result:

 text_agg |      bytea_agg       | bytea_agg_text
----------+----------------------+----------------
 mouse    | \x6d2e6f2e752e732e65 | m.o.u.s.e

jsonb_agg()

This aggregate function, together with json_agg(), are described fully in the jsonb_agg() section within the overall JSON section.

Signature:

input value:       anyelement

return value:      jsonb

Purpose: Returns a JSON array whose elements are the JSON values formed from each of the to-be-aggregated values.

jsonb_object_agg

This aggregate function, together with json_object_agg(), are described fully in the jsonb_object_agg() section within the overall JSON section.

Signature:

input value:       "any", "any"

return value:      jsonb

Purpose: Returns a JSON object whose fields are the JSON elements formed by each of the to-be-aggregated value pairs. The first value in the pair provides the name (key) of the field and the second value in the pair provides its value. Because the order of key-value pairs in a JSON object is undefined (values are addressed by their key), the aggregation order has no effect on the result.

Examples

Each of these aggregate functions is invoked by using the same syntax—either the GROUP BY syntax or the OVER syntax. First create and populate the test table:

drop table if exists t cascade;
create table t(
  k     int   primary key,
  class int   not null,
  v     text  not null);

insert into t(k, class, v)
select
  (1 + s.v),
  case (s.v) < 3
    when true then 1
              else 2
  end,
  chr(97 + s.v)
from generate_series(0, 5) as s(v);

select k, class, v from t order by k;

This is the result:

 k | class | v
---+-------+---
 1 |     1 | a
 2 |     1 | b
 3 |     1 | c
 4 |     2 | d
 5 |     2 | e
 6 |     2 | f

GROUP BY syntax

Try this:

select
  class,
  array_agg(v            order by k desc) filter (where v <> 'b') as "array_agg(v)",
  string_agg(v, ' ~ '    order by k desc) filter (where v <> 'e') as "string_agg(v)",
  jsonb_agg(v            order by v desc) filter (where v <> 'b') as "jsonb_agg",
  jsonb_object_agg(v, k  order by v desc) filter (where v <> 'e') as "jsonb_object_agg(v, k)"
from t
group by class
order by class;

This is the result:

 class | array_agg(v) | string_agg(v) |    jsonb_agg    |  jsonb_object_agg(v, k)
-------+--------------+---------------+-----------------+--------------------------
     1 | {c,a}        | c ~ b ~ a     | ["c", "a"]      | {"a": 1, "b": 2, "c": 3}
     2 | {f,e,d}      | f ~ d         | ["f", "e", "d"] | {"d": 4, "f": 6}

As promised, the result produced by jsonb_object_agg() is insensitive to order.

OVER syntax

Try this:

select
  class,
  (array_agg(v)          filter (where v <> 'b')   over w) as "array_agg(v)",
  (string_agg(v, ' ~ ')  filter (where v <> 'b')   over w) as "string_agg(v)",
  (jsonb_agg(v)          filter (where v <> 'b')   over w) as "string_agg(v)",
  (jsonb_object_agg(v, k) filter (where v <> 'e')  over w) as  "jsonb_object_agg(v, k)"
from t
window w as (
  partition by class
  order by k desc
  range between unbounded preceding and current row)
order by 1;

This is the result:

 class | array_agg(v) | string_agg(v) |  string_agg(v)  |  jsonb_object_agg(v, k)
-------+--------------+---------------+-----------------+--------------------------
     1 | {c}          | c             | ["c"]           | {"c": 3}
     1 | {c}          | c             | ["c"]           | {"b": 2, "c": 3}
     1 | {c,a}        | c ~ a         | ["c", "a"]      | {"a": 1, "b": 2, "c": 3}
     2 | {f}          | f             | ["f"]           | {"f": 6}
     2 | {f,e}        | f ~ e         | ["f", "e"]      | {"f": 6}
     2 | {f,e,d}      | f ~ e ~ d     | ["f", "e", "d"] | {"d": 4, "f": 6}