Reputation: 954
I have a table in the following format :
owner_id | question | response | created_at |
---|---|---|---|
1 | dog_name | Rex | 2021-05-31 |
1 | cat_name | Old Kitty | 2021-05-01 |
1 | cat_name | New Kitty | 2021-05-30 |
2 | cat_name | Bella | 2021-05-28 |
3 | bird_name | Birdy | 2021-05-28 |
And I would like to be able have the list of owner with each pet's name :
owner_id | dog_name | cat_name | bird_name |
---|---|---|---|
1 | Rex | New Kitty | NULL |
2 | NULL | Bella | NULL |
3 | NULL | NULL | Birdy |
I have manage to list all the data that I need with the following query :
SELECT DISTINCT ON (owner_id, question) owner_id, question, response
FROM pets
ORDER BY owner_id, question, created_at DESC
It output what I need. However, I need to transpose the data and having dog_name
/ cat_name
/ etc. (finite list of pet type) in column.
Upvotes: 0
Views: 40
Reputation: 1269773
You can use aggregation and arrays for this:
select owner_id,
(array_agg(response order by created_at desc) filter (where question = 'dog_name'))[1],
(array_agg(response order by created_at desc) filter (where question = 'cat_name'))[1],
(array_agg(response order by created_at desc) filter (where question = 'bird_name'))[1]
from t
group by owner_id;
Here is a db<>fiddle.
Another method is conditional aggregation after retrieving the most recent row for each owner/question combination:
select owner_id,
max(response) filter (where question = 'dog_name'),
max(response) filter (where question = 'cat_name'),
max(response) filter (where question = 'bird_name')
from (select distinct on (owner_id, question) t.*
from t
order by owner_id, question, created_at desc
) t
group by owner_id;
Upvotes: 1
Reputation: 6454
You could use CTE's. Easy to read, but this could get pretty long if having multiple pet names. Not sure how you want to handle occasions where someone has more than one pet of same species, so that would impact this answer, too.
with dogs as
(select distinct owner_id, response
from pets
where question = 'dog_name'
),
cats as
(select distinct owner_id, response
from pets
where question = 'cat_name'
),
birds as
(select distinct owner_id, response
from pets
where question = 'bird_name'
)
select distinct p.owner_id,
d.response as dog_name,
c.response as cat_name,
b.response as bird_name
from pets p
left join dogs d
on d.owner_id = p.owner_id
left join cats c
on c.owner_id = p.owner_id
left join birds b
on b.owner_id = p.owner_id
Based on your comment (using the most recent response), I modified the answser as:
with dogs as
(select distinct owner_id, response,
rank() over (partition by owner_id order by created_at desc) as p_rank
from pets
where question = 'dog_name'
),
cats as
(select distinct owner_id, response,
rank() over (partition by owner_id order by created_at desc) as p_rank
from pets
where question = 'cat_name'
),
birds as
(select distinct owner_id, response,
rank() over (partition by owner_id order by created_at desc) as p_rank
from pets
where question = 'bird_name'
)
select distinct p.owner_id,
d.response as dog_name,
c.response as cat_name,
b.response as bird_name
from pets p
left join dogs d
on d.owner_id = p.owner_id
left join cats c
on c.owner_id = p.owner_id
left join birds b
on b.owner_id = p.owner_id
where (d.p_rank is null or d.p_rank = 1)
and (c.p_rank is null or c.p_rank = 1)
and (b.p_rank is null or b.p_rank = 1)
Upvotes: 1