如何使用动态字段在PostgreSQL中进行透视/交叉表

时间:2013-12-16 13:09:25

标签: postgresql pivot crosstab

我是PostgreSQL的新手。

id | customer_id | form_id | field_id | field_name | form_submission_id |    value     |
---+-------------+---------+----------+------------+--------------------+--------------+
 1 |           2 |       7 | c313     | Program    |                  1 | 2013         |            
 2 |           2 |       7 | c313     | Program    |                  2 | PIP          |            
 3 |           2 |       7 | c313     | Program    |                  3 | CIP          |            
 4 |           2 |       7 | c343     | Broker     |                  1 | broker test  |
 5 |           2 |       7 | c343     | Broker     |                  2 | broker test1 |            
 6 |           2 |       7 | c343     | Broker     |                  3 | broker test2 |            
 7 |           2 |       7 | c339     | Class      |                  1 | Class test   |             
 8 |           2 |       7 | c339     | Class      |                  2 | Class test1  |            
 9 |           2 |       7 | c339     | Class      |                  3 | Class test2  |    

我想要

这样的记录
customer_id form_id Program      Broker        Class        form_submission_id 
  2          7        2013       broker test   Class test        1
  2          7        PIP        broker test1  Class test1       1
  2          7        CIP        broker test2  Class test3       1

field_name值将是动态的,而不是固定值。

我试过这个,但是收到错误如'错误:返回和sql元组描述不兼容'

select * from crosstab (
    'select Distinct customer_id ,form_id , field_name from form_submissions_reports '
    )
    as newtable (
     customer_id integer,form_id integer,field_id1 varchar,field_id2 varchar,field_id3 varchar
    ); 

但重要的是它的字段名称是动态的。

1 个答案:

答案 0 :(得分:1)

设置示例:

create table form_submissions_reports (id integer, customer_id integer, 
form_id integer, field_id text, field_name text, form_submission_id integer,
"value" text);


insert into form_submissions_reports
select 1 ,2 ,7 , 'c313', 'Program',1 , '2013' union all
select 2 ,2 ,7 , 'c313', 'Program',2 , 'PIP' union all
select 3 ,2 ,7 , 'c313', 'Program',3 , 'CIP' union all
select 4 ,2 ,7 , 'c343', 'Broker',1 , 'broker test' union all
select 5 ,2 ,7 , 'c343', 'Broker',2 , 'broker test1' union all
select 6 ,2 ,7 , 'c343', 'Broker',3 , 'broker test2' union all
select 7 ,2 ,7 , 'c339', 'Class',1 , 'Class test' union all
select 8 ,2 ,7 , 'c339', 'Class',2 , 'Class test1' union all
select 9 ,2 ,7 , 'c339', 'Class',3 , 'Class test2';

这是您尝试过固定列数的解决方案:

select * 
from crosstab (
'select Distinct form_submission_id , customer_id ,form_id , field_name, value 
from form_submissions_reports order by form_submission_id ', 
'select Distinct field_name from form_submissions_reports'
)as newtable (
              form_submission_id integer, customer_id integer,form_id integer,field_id 
              varchar,field_id2 varchar,field_id3 varchar
              ); 

就我的经验而言,关系数据库不是为没有按列数及其类型修复的类型构建的。所以你必须作弊。

这只对娱乐有益。我没有在这个完全“动态”的解决方案(灵活的列数,数据驱动的名称)中看到任何生产价值:

1)创建这两个函数:

CREATE OR REPLACE FUNCTION foo()
RETURNS text as
$BODY$
DECLARE
dynamic_columns text;
BEGIN

select array_to_string(array_agg(distinct  field_name||' text'), ', ') into dynamic_columns from form_submissions_reports;

return 'select * from crosstab (
        ''select Distinct form_submission_id , customer_id ,form_id , field_name, value from form_submissions_reports order by form_submission_id '', 
    ''select Distinct field_name from form_submissions_reports''
    )
    as newtable (
     form_submission_id integer, customer_id integer,form_id integer, '|| dynamic_columns ||'
    )';


END;
$BODY$
LANGUAGE plpgsql;


CREATE OR REPLACE FUNCTION bar()
RETURNS void as
$BODY$
DECLARE
dyn_crosstab text;
BEGIN

DROP VIEW IF EXISTS barview;

select foo() into dyn_crosstab;

execute 'create view barview as '||dyn_crosstab;

END;
$BODY$
LANGUAGE plpgsql;

2)执行bar()函数。这将为您提供新版“barview”视图。之后,查询barview。

select bar();
select * from barview;

3)要测试它是否是动态的,请为field_name插入一个具有不同值的新行,然后重复2):

INSERT INTO form_submissions_reports
(
  id,  customer_id,  form_id,  field_id,
  field_name,  form_submission_id,  value
)
VALUES(  10,  2,  7,  'd',  'NEWFIELD',  4,
  'newfield test');

select bar();
select * from barview;