array_agg
PG 8.4+→ anyarrayCollects all non-NULL input values into an array. Supports ORDER BY to control element order.
Signature
array_agg ( expression [ORDER BY ...] ) → arrayParameters
| Parameter | Type | Description |
|---|---|---|
| expression | any | Value to collect; NULLs are excluded |
Examples
SELECT array_agg(name ORDER BY name) FROM tags;{css,html,javascript}SELECT user_id, array_agg(tag) FROM user_tags GROUP BY user_id;Array of tags per userSELECT array_agg(DISTINCT status) FROM orders;{cancelled,completed,pending}SELECT author_id, array_agg(title ORDER BY published_at DESC) AS recent_books FROM books GROUP BY author_id;Books per author sorted newest-firstWithout an explicit `ORDER BY`, `array_agg` produces elements in an undefined, non-deterministic order that can change between query executions, PostgreSQL versions, or after autovacuum.
✓ Instead: Always specify `array_agg(col ORDER BY col)` or an appropriate sort key when the order of array elements matters downstream.
Use `array_agg` to fetch parent + children in one query: `SELECT u.id, array_agg(t.name) AS tags FROM users u JOIN user_tags t ON t.user_id = u.id GROUP BY u.id`. This avoids N+1 queries.
SELECT author_id, array_agg(title ORDER BY published_at) AS books FROM books GROUP BY author_id;All book titles per author in chronological orderCommon Gotchas
string_agg without ORDER BY produces non-deterministic results
string_agg(col, ',') returns results in arbitrary order unless you specify ORDER BY inside the aggregate.
COUNT(col) skips NULLs but COUNT(*) does not
COUNT(column_name) counts non-NULL values only. COUNT(*) counts all rows. These produce different results when the column has NULLs.
Arrays are 1-indexed in PostgreSQL, not 0-indexed
PostgreSQL arrays start at index 1 by default. array[0] returns NULL, not the first element — silently wrong.