Reputation: 636
I am using the following schema:
CREATE TABLE person (
person_name VARCHAR PRIMARY KEY
);
CREATE TABLE pet (
animal_name VARCHAR,
person_name VARCHAR REFERENCES person(person_name),
PRIMARY KEY (animal_name, person_name)
);
I wish to create a table where, for each person_name
, I get an array with the pets of that person. I am using PostgreSQL 9.3.4.
I have the following values in each table:
Person
PERSON_NAME
-----------
Alice
Bob
Pet
ANIMAL_NAME | PERSON_NAME
-------------------------
Woof | Alice
Meow | Alice
I wish to create the following table:
PERSON_NAME | PETS
--------------------------
Alice | {Woof, Meow}
Bob | {}
I cannot, however, create the empty array. What I get is the following:
PERSON_NAME | PETS
--------------------------
Alice | {Woof, Meow}
Bob | {NULL}
This is the query I am using:
SELECT
person.person_name,
array_agg(pet.animal_name) AS pets
FROM
person LEFT JOIN pet ON person.person_name = pet.person_name
GROUP BY
person.person_name;
I understand why I am getting the array with the NULL
value inside, I want to know how to get an empty array instead.
Here is a fiddle with the code needed to create the schema, insert the values and with the query I am using. The result shown in the website doesn't show the NULL
value, although it is there.
EDIT
The result will be parsed to JSON, that is why {NULL}
is not an acceptable result, as it will be parsed to [null]
, which is different from the []
I require. For the same reason, something like {""}
is not an acceptable result either.
Upvotes: 17
Views: 32885
Reputation: 1
You can create and use an empty array with these ways below. *My answer explains how to create and use an array:
SELECT ARRAY[]::VARCHAR[]; -- {}
SELECT (ARRAY[]::VARCHAR[])[1]; -- NULL
SELECT (ARRAY[]::VARCHAR[])[2]; -- NULL
SELECT (ARRAY[]::VARCHAR[])[3]; -- NULL
*Memos:
::VARCHAR[]
, there is the error.Or:
SELECT '{}'::VARCHAR[]; -- {}
SELECT ('{}'::VARCHAR[])[1]; -- NULL
SELECT ('{}'::VARCHAR[])[2]; -- NULL
SELECT ('{}'::VARCHAR[])[3]; -- NULL
*Memos:
::VARCHAR[]
, the value above is not an array and the type of the value above is unknown
.Or:
SELECT string_to_array('','')::VARCHAR[]; -- {}
SELECT (string_to_array('','')::VARCHAR[])[1]; -- NULL
SELECT (string_to_array('','')::VARCHAR[])[2]; -- NULL
SELECT (string_to_array('','')::VARCHAR[])[3]; -- NULL
*Memos:
::VARCHAR[]
, the type of the array above is TEXT[]
.Upvotes: -1
Reputation: 36274
The most simple way of doing this, is to use the ARRAY
constructor's sub-query variant:
SELECT
person.person_name,
ARRAY(SELECT animal_name FROM pet WHERE person.person_name = pet.person_name) AS pets
FROM
person;
Upvotes: 2
Reputation: 434965
Two possibilities come to mind.
One option is a UNION.
select person.person_name, array_agg(pet.animal_name) as pets
from person
join pet on person.person_name = pet.person_name
group by person.person_name
union
select person.person_name, array[]::text[] as pets
from person
left join pet on person.person_name = pet.person_name
where pet.animal_name is null
The first part uses a JOIN to get the people with pets, then the second grabs those poor people that don't have any pets at all. Separating them lets you supply a literal empty array for the pet-less people.
Another option would be to do pretty much the same thing using a LEFT JOIN to a derived table:
with pet_names as (
select person.person_name, array_agg(pet.animal_name) as pets
from person
join pet on person.person_name = pet.person_name
group by person.person_name
)
select p.person_name, coalesce(n.pets, array[]::text[])
from person p
left join pet_names n on p.person_name = n.person_name
This one is a little more natural to me as it lets you use COALESCE (the first thing you reach for you want to map NULL to something else) to supply the empty array.
Updated demo: http://sqlfiddle.com/#!15/24ccc/7
There may be other solutions, these two just seem like natural approaches to me.
Upvotes: 4
Reputation: 62861
You can use COALESCE
to replace NULL
values:
SELECT
person.person_name,
array_agg(coalesce(pet.animal_name,'')) AS pets
FROM
person LEFT JOIN pet ON person.person_name = pet.person_name
GROUP BY
person.person_name
;
Upvotes: 1