Reputation: 42109
Conditions: PostgreSQL 8.4 (this would be too simple with 9.x)
Table:
id | gpa | class | rank
----+-----+---------+--------
1 | 2.0 | english | low
1 | 2.0 | math | low
1 | 2.0 | pe | low
1 | 2.0 | spanish | medium
2 | 3.5 | english | high
2 | 3.5 | history | high
2 | 3.5 | art | great
2 | 3.5 | tech | high
3 | 4.0 | pe | medium
3 | 4.0 | spanish | high
3 | 4.0 | english | great
3 | 4.0 | art | great
Wanted:
id | gpa | great | high | medium | low
----+-----+--------------+------------------------+---------+-----
1 | 2.0 | | | spanish | english, math, pe
2 | 3.5 | art | english, history, tech | |
3 | 4.0 | art, english | spanish | pe |
Current Method:
WITH details AS (
select * from table order by rank, class
)
SELECT id
, gpa
, array_to_string(array_agg(CASE WHEN rank='great' THEN class END,', ')) as great
, array_to_string(array_agg(CASE WHEN rank='high' THEN class END,', ')) as high
, array_to_string(array_agg(CASE WHEN rank='medium' THEN class END,', ')) as medium
, array_to_string(array_agg(CASE WHEN rank='low' THEN class END,', ')) as low
FROM details
ORDER BY gpa;
So I tried to put this as an example from what I'm doing - no I don't have an actual table with this structure that isn't much normalized, but I do have a subquery that produces something like this.
In actuality my array_agg() is also concatenating two fields (a word and a number) and i'm sorting by the number e.g.(array_agg(CASE ... THEN foo || ' - ' || bar
), unfortunately my output is only mostly sorted. Perhaps I can improve this question if I have more time.
Upvotes: 2
Views: 1713
Reputation:
When I execute this query:
with details AS (
select * from table order by class
)
select t.id, t.gpa,
(select array_to_string(array_agg(class),',')
from details d
where d.id = t.id and d.gpa = t.gpa and rank = 'great')
as great,
(select array_to_string(array_agg(class),',')
from details d
where d.id = t.id and d.gpa = t.gpa and rank = 'high')
as high,
(select array_to_string(array_agg(class),',')
from details d
where d.id = t.id and d.gpa = t.gpa and rank = 'medium')
as medium,
(select array_to_string(array_agg(class),',')
from details d
where d.id = t.id and d.gpa = t.gpa and rank = 'low')
as low
from table t
group by t.id, t.gpa
order by t.gpa
I get the results:
id gpa great high medium low
----+-----+-------------+----------------------+----------+------------------
1 2.0 spanish english,math,pe
2 3.5 art english,history,tech
3 4.0 art,english spanish pe
I hope you serve.
UPDATE: Other option with a better performance.
select t.id, t.gpa,
a.class as great,
b.class as high,
c.class as medium,
d.class as low
from table t
left join (select id, gpa, array_to_string(array_agg(class),',') as class
from table
where rank = 'great'
group by id, gpa
) a on (a.id = t.id and a.gpa = t.gpa)
left join (select id, gpa, array_to_string(array_agg(class),',') as class
from table
where rank = 'high'
group by id, gpa
) b on (b.id = t.id and b.gpa = t.gpa)
left join (select id, gpa, array_to_string(array_agg(class),',') as class
from table
where rank = 'medium'
group by id, gpa
) c on (c.id = t.id and c.gpa = t.gpa)
left join (select id, gpa, array_to_string(array_agg(class),',') as class
from table
where rank = 'low'
group by id, gpa
) d on (d.id = t.id and d.gpa = t.gpa)
group by t.id, t.gpa, a.class, b.class, c.class, d.class
order by t.gpa
Upvotes: 1
Reputation: 7307
Create a function like:
create or replace function sort(anyarray) returns anyarray language sql as
$$
select array_agg(x) from(
select unnest($1) as x order by 1
) tab;
$$;
and then:
...
array_to_string(sort(array_agg(CASE WHEN rank='great' THEN class END,', ')))
...
Upvotes: 1