2

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.

2 Answers 2

1

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
Sign up to request clarification or add additional context in comments.

6 Comments

So this is something I was initially thinking about, but wouldn't it have a huge performance hit? It's running 4 queries for each record
Hi @vol7ron, you're right. This isn't the best option due to huge performance hit. However I read that maybe you don't have permission to create a function. If you confirm that you have it, I can write other options with better performance.
@vol7ron, I just update the answer with a second query with better performance.
that's what I thought of second - I think it's the best solution. I upvoted maniek, but in order have selected this as the answer (haven't tested it, but left join's should work)
Thank you. With the second query I have obtained the same results as with the first.
|
1

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,', '))) 
...

1 Comment

So basically a subquery sort. not sure what permissions i have, I'll check if i can create a function

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.