Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Postgres returns [null] instead of [] for array_agg of join table

I'm selecting some objects and their tags in Postgres. The schema is fairly simple, three tables:

objects id

taggings id | object_id | tag_id

tags id | tag

I'm joining the tables like this, using array_agg to aggregate the tags into one field:

SELECT objects.*,     array_agg(tags.tag) AS tags, FROM objects LEFT JOIN taggings ON objects.id = taggings.object_id LEFT JOIN tags ON tags.id = taggings.tag_id 

However, if the object has no tags, Postgres returns this:

[ null ] 

instead of an an empty array. How can I return an empty array when there are no tags? I have double checked that I don't have a null tag being returned.

The aggregate docs say "The coalesce function can be used to substitute zero or an empty array for null when necessary". I tried COALESCE(ARRAY_AGG(tags.tag)) as tags but it still returns an array with null. I have tried making the second parameter numerous things (such as COALESCE(ARRAY_AGG(tags.tag), ARRAY()), but they all result in syntax errors.

like image 500
Andy Ray Avatar asked Jun 29 '15 06:06

Andy Ray


People also ask

What does Array_agg return?

The ARRAY_AGG() accepts an expression that returns a value of any type which is valid for an array element. The ORDER BY clause is an optional clause. It specifies the order of rows processed in the aggregation, which determines the order of the elements in the result array.

What is Array_agg in Postgres?

PostgreSQL ARRAY_AGG() function is an aggregate function that accepts a set of values and returns an array where each value in the input set is assigned to an element of the array.


2 Answers

Another option might be array_remove(..., NULL) (introduced in 9.3) if tags.tag is NOT NULL (otherwise you might want to keep NULL values in the array, but in that case, you can't distinguish between a single existing NULL tag and a NULL tag due to the LEFT JOIN):

SELECT objects.*,      array_remove(array_agg(tags.tag), NULL) AS tags, FROM objects LEFT JOIN taggings ON objects.id = taggings.object_id LEFT JOIN tags ON tags.id = taggings.tag_id 

If no tags are found, an empty array is returned.

like image 102
Thomas Perl Avatar answered Oct 20 '22 20:10

Thomas Perl


Since 9.4 one can restrict an aggregate function call to proceed only rows that match a certain criterion: array_agg(tags.tag) filter (where tags.tag is not null)

like image 44
Alexey Bashtanov Avatar answered Oct 20 '22 21:10

Alexey Bashtanov