I have a table that looks like this
CREATE TABLE foo (id, name, category)
AS VALUES
( 1, 'name1.1', 'cat1.1.1'),
( 1, 'name1.2', 'cat1.1.1'),
( 1, 'name1.3', 'cat1.2.1'),
( 2, 'name2.1', 'cat2.1.1'),
( 2, 'name2.2', 'cat2.1.1'),
( 3, 'name3.1', 'cat3.1.1')
;
I'm trying to get a result that looks like this,
Id | name1 | name2 | name3 |
---|---|---|---|
1 | name1.1 | name1.2 | name1.3 |
2 | name2.1 | name2.2 | |
3 | name3.1 |
I would aggregate all names into an array, then extract the array elements as columns:
select id,
names[1] as name1,
names[2] as name2,
names[3] as name3
from (
select id,
array_agg(name order by name) as names
from foo
group by id
) t
If the names could contain things like name10.11
then the order of the columns won't be numerical because the string '10'
is lower than the string '2'
. If you want the order to reflect the numbers, the sorting gets a bit more complicated:
array_agg(name order by string_to_array(replace(name, 'name', ''), '.')::int[]) as names
This removes the name
prefix and converts the numbers to an integer array which then sorts properly. Another option is to remove everything that's not a digit or dot:
array_agg(name order by string_to_array(regexp_replace(name, '[^0-9.]', '', 'g'), '.')::int[]) as names