STRING_AGG() Function in PostgreSQL

In PostgreSQL, we can use the STRING_AGG() function to return columns from a query as a delimited list.

Syntax

The syntax goes like this:

string_agg ( value text, delimiter text ) → text
string_agg ( value bytea, delimiter bytea ) → bytea

We can also use the ORDER BY clause and a DISTINCT clause from within this function, which affects the output from the function. More on this below.

Example

Suppose we run the following query:

SELECT PetName 
FROM Pets;

And we get the following result:

+---------+
| petname |
+---------+
| Fluffy  |
| Fetch   |
| Scratch |
| Wag     |
| Tweet   |
| Fluffy  |
| Bark    |
| Meow    |
+---------+
(8 rows)

We can use STRING_AGG() to return all of those rows as a delimited list.

To do this, pass the PetName column as the first argument, and our chosen delimiter as the second argument:

SELECT STRING_AGG(PetName, ',') 
FROM Pets;

Result:

+-------------------------------------------------+
|                   string_agg                    |
+-------------------------------------------------+
| Fluffy,Fetch,Scratch,Wag,Tweet,Fluffy,Bark,Meow |
+-------------------------------------------------+
(1 row)

Changing the Delimiter

In the previous example, I chose a comma as the delimiter. Here it is with a different delimiter:

SELECT STRING_AGG(PetName, '-') 
FROM Pets;

Result:

Fluffy-Fetch-Scratch-Wag-Tweet-Fluffy-Bark-Meow

We can even use an empty string to remove all separators (so that the values are concatenated):

SELECT STRING_AGG(PetName, '') 
FROM Pets;

And we get the following result:

FluffyFetchScratchWagTweetFluffyBarkMeow

Ordering

We can use the ORDER BY clause within the STRING_AGG() function to order its own output:

SELECT STRING_AGG(PetName, ',' ORDER BY PetName ASC) FROM Pets;

Result:

Bark,Fetch,Fluffy,Fluffy,Meow,Scratch,Tweet,Wag

That was in ascending order.

Here it is in descending order:

SELECT STRING_AGG(PetName, ',' ORDER BY PetName DESC) FROM Pets;

Result:

Wag,Tweet,Scratch,Meow,Fluffy,Fluffy,Fetch,Bark

Note that this only sorts the output of the STRING_AGG() function – it’s completely independent of any ordering applied to the SELECT statement itself.

The DISTINCT Clause

We can use the DISTINCT clause to return unique values. In other words, if there are duplicate values, only one occurrence is returned:

SELECT STRING_AGG(DISTINCT PetName, ',' ORDER BY PetName ASC) FROM Pets;

Result:

Bark,Fetch,Fluffy,Meow,Scratch,Tweet,Wag

In this case, Fluffy only appears once. When we run it without the DISTINCT clause, Fluffy appears twice:

SELECT STRING_AGG(PetName, ',' ORDER BY PetName ASC) FROM Pets;

Result:

Bark,Fetch,Fluffy,Fluffy,Meow,Scratch,Tweet,Wag

Grouped Query Results

We can include STRING_AGG() in a query with a GROUP BY clause to achieve a result like this:

SELECT 
    PetTypeId,
    STRING_AGG(PetName, ',' ORDER BY PetName ASC)
FROM Pets
GROUP BY PetTypeId
ORDER BY PetTypeId;

Result:

+-----------+-----------------------+
| pettypeid |      string_agg       |
+-----------+-----------------------+
|         1 | Tweet                 |
|         2 | Fluffy,Meow,Scratch   |
|         3 | Bark,Fetch,Fluffy,Wag |
+-----------+-----------------------+

In my database, the actual pet type names are in another table called PetTypes. We could therefore run an INNER JOIN on the PetTypes table to get the actual pet type names:

SELECT 
    pt.PetType,
    STRING_AGG(p.PetName, ',' ORDER BY p.PetName ASC)
FROM Pets p
INNER JOIN PetTypes pt ON
p.PetTypeId = pt.PetTypeId
GROUP BY pt.PetType
ORDER BY pt.PetType ASC;

Result:

+---------+-----------------------+
| pettype |      string_agg       |
+---------+-----------------------+
| Bird    | Tweet                 |
| Cat     | Fluffy,Meow,Scratch   |
| Dog     | Bark,Fetch,Fluffy,Wag |
+---------+-----------------------+