PostgreSQL: Каков наилучший способ сортировки нескольких array_agg () с различными условиями? - PullRequest
2 голосов
/ 11 февраля 2012

Условия: PostgreSQL 8.4 (было бы слишком просто с 9.x)

Таблица:

  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

Требуется:

  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      |  

Текущий метод:

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;

Поэтому я попытался привести это в качестве примера из того, что я делаю - нет, у меня нет реальной таблицы с этой структурой, которая не очень нормализована, но у меня есть подзапрос, которыйпроизводит что-то вроде этого.

На самом деле мой array_agg () также объединяет два поля (слово и число), и я сортирую по числу, например (array_agg(CASE ... THEN foo || ' - ' || bar), к сожалению, мой вывод только в основномотсортирован.Возможно, я смогу улучшить этот вопрос, если у меня будет больше времени.

Ответы [ 2 ]

1 голос
/ 12 февраля 2012

Когда я выполняю этот запрос:

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

Я получаю результаты:

 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

Я надеюсь, что вы служите.

ОБНОВЛЕНИЕ: Другой вариант с лучшей производительностью.

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
1 голос
/ 12 февраля 2012

Создайте функцию вроде:

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;
$$;

и затем:

... 
array_to_string(sort(array_agg(CASE WHEN rank='great'  THEN class END,', '))) 
...
...