2017-01-09 12 views
2

У меня есть таблица в Postgres, которая была предназначена для захвата информации в неструктурированной форме и ее восстановления. Мне нужно повторно применить некоторую структуру при экспорте данных из этой таблицы, и я боюсь.Поворот произвольно многих строк в столбцы в PostgreSQL

В настоящее время у меня есть таблица вида:

lbl | name  | value 
----|------------|-------- 
1 | num  |  1 
1 | colour  | "Red" 
1 | percentage | 25.0 
2 | num  |  2 
2 | colour  | "Green" 
2 | percentage | 50.0 
3 | num  |  3 
3 | colour  | "Blue" 
3 | percentage | 75.0 

И мне нужно, чтобы создать таблицу в таком виде:

lbl | num | colour | percentage 
----|-----|---------|------------ 
1 | 1 | "Red" | 25.0 
2 | 2 | "Green" | 50.0 
3 | 3 | "Blue" | 75.0 

Я построил этот запрос:

SELECT lbl, 
    max(case when name = 'num' then value else '-' end) num, 
    max(case when name = 'colour' then value else '-' end) colour, 
    max(case when name = 'percentage' then value else '-' end) percentage 
FROM example_table 
GROUP BY lbl 

Запрос работает, но мне нужно его расширить, чтобы включить произвольное количество потенциальных значений для имени. Я исследовал crossfunc, но не смог заставить его работать так, как я предполагал. Любая помощь будет принята с благодарностью.

Я создал sqlfiddle здесь, чтобы помочь пнуть вещи: http://sqlfiddle.com/#!9/8d3133/6/0

редактировать: Я могу использовать PL/PgSQL также, если это делает это возможным.

+0

Я думаю, вам нужно использовать динамический SQL здесь. –

+0

"* включает произвольное количество потенциальных значений для имени *", что невозможно в простом SQL. Количество столбцов запроса должно быть известно в базе данных _before_, выполняющей запрос. –

+0

ах, хорошо. Если у меня есть доступ к использованию plsql, это может сработать? – George

ответ

4

Основная проблема с сводными таблицами в Postgres является то, что структура результата (число и имена столбцов) из запроса не могут изменяться в зависимости от выбранных данных. Одним из возможных решений является динамическое создание представления, структура которого определяется данными. Пример функции создает представление на основе таблицы example_table:

create or replace function create_pivot_view() 
returns void language plpgsql as $$ 
declare 
    list text; 
begin 
    select string_agg(format('jdata->>%1$L "%1$s"', name), ', ') 
    from (
     select distinct name 
     from example_table 
     ) sub 
    into list; 

    execute format($f$ 
     drop view if exists example_pivot_view; 
     create view example_pivot_view as 
     select lbl, %s 
     from (
      select lbl, json_object_agg(name, value) jdata 
      from example_table 
      group by 1 
      order by 1 
      ) sub 
     $f$, list); 
end $$; 

Используйте функцию после того, как таблица модифицируется (возможно, в качестве триггера) и запросить созданный вид:

select create_pivot_view(); 

select * 
from example_pivot_view; 

lbl | num | colour | percentage 
-----+-----+--------+------------ 
    1 | 1 | Red | 25.0 
    2 | 2 | Green | 50.0 
    3 | 3 | Blue | 75.0 
(3 rows) 

Test it here.

Обратите внимание, что необходимо воссоздать представление (вызвать функцию) только после того, как в таблицу добавлено новое имя (или некоторое имя удалено из него). Если набор разных имен не изменяется, вы можете запросить представление без его воссоздания. Если набор изменяется часто, создание временного представления было бы лучшим вариантом.

Вы можете быть также заинтересованы в Flatten aggregated key/value pairs from a JSONB field?

+1

Как насчет параллелизма?Измените 'create view example_pivot_view as' на' declare example_pivot_view cursor for' и 'select * from example_pivot_view;' для 'fetch all from example_pivot_view;' - это будет намного безопаснее. – Abelisto

+0

Курсор - хорошая идея. Я не предлагаю этого в основном потому, что общие знания о правильном использовании курсоров минимальны, решение достаточно сложно для среднего читателя. – klin

+0

Спасибо, что сработало удовольствие. Сначала я тестировал с видом, а во-вторых, с курсором, и оба работали. Существуют и другие компоненты наших инструментов, которые работают с курсорами, поэтому я не совсем им незнакома. – George

0

попробовать это

select 
tab.ibl, 
t1_num.value as "num", 
t2_color.value as "colour", 
t3_perc.value as "percentage" 
from 
(
    select distinct ibl from your_table order by tab.ibl desc 
) tab 
left join your_table t1_num on t1_num.ibl = tab.ibl and t1_num.name = 'num' 
left join your_table t2_color on t2_color.ibl = tab.ibl and t2_color.name = 'colour' 
left join your_table t3_perc on t3_perc.ibl = tab.ibl and t3_perc.name = 'percentage' 
+0

Спасибо за ответ, но это все еще требует знания того, что потенциальные значения столбца имени превышают время. – George