PostgreSQL - string_agg with limited number of elements

I am not aware that you can limit it in the string_agg() function. You can limit it in other ways:

select postid, string_agg(distinct(tag), ', ')
from table t
group by postid

Then you can do:

select postid, string_agg(distinct (case when seqnum <= 10 then tag end), ', ')
from (select t.*, dense_rank() over (partition by postid order by tag) as seqnum
      from table t
     ) t
group by postid

Nest your string_agg clause in a split_part, including the delimiter as the second argument, and the number of elements you need as the final argument. Like so:

split_part(string_agg(distinct(tag),', '), ', ', 1)

There are two more ways.

1) make an array from rows, limit it, and then concatenate into string:

SELECT array_to_string((array_agg(DISTINCT tag))[1:3], ', ') FROM tbl

("array[1:3]" means: take items from 1 to 3 from array)

2) concatenate rows into string without limit, then use "substring" to trim it:

string_agg(distinct(tag),',')

If you know that your "tag" field cannot contain , character then you can select all text before nth occurence of your ,

SELECT substring(
string_agg(DISTINCT tag, ',')
from '(?:[^,]+,){1,3}')
FROM tbl

This substring will select 3 or less strings divided by ,


To "limit the number of elements in the following string_agg()", use LIMIT in a subquery:

SELECT string_agg(tag, ', ') AS tags
FROM  (
   SELECT DISTINCT tag
   FROM   tbl
-- ORDER  BY tag  -- optionally order to get deterministic result
   LIMIT   123    -- add your limit here
   ) sub;

The subquery is no problem for performance at all. On the contrary, this is typically faster, even if you don't impose a maximum number with LIMIT, because the group-wise DISTINCT in the aggregate function is more expensive than doing it in a subquery for all rows at once.

Or, to get the "100 most common tags":

SELECT string_agg(tag, ', ') AS tags
FROM  (
   SELECT tag
   FROM   tbl
   GROUP  BY tag
   ORDER  BY count(*) DESC
   LIMIT  100
   ) sub;