PostgreSQL中crosstab实现动态行转列(附案例)

行转列,通用做法是使用 case when 。today,介绍一下新的方法 -- crosstab

crosstab 函数被用来生成 pivot(扭曲,转动)展示,即通过横向而不是下拉展示(行转列)。如下面这个例子:

源表:

row1    val11
row1    val12
row1    val13
...
row2    val21
row2    val22
row2    val23
...

期望的结果:

row1    val11   val12   val13   ...
row2    val21   val22   val23   ...
...

扩展tablefunc

PostgreSQL 提供了一个 tablefunc 模块,内置了多个函数,其中就有 crosstab(交叉表,又叫行转列,或者长表转宽表) 

--prerequisite: 安装插件tablefunc
create extension tablefunc;

crosstab声明该函数返回setof record,因此必须在FROM在调用SELECT语句的子句中定义输出列的实际名称和类型,例如: 

SELECT * FROM crosstab('...') AS ct(row_name text, _col1 text, _col2 text);

 产生一个类似于的集合:

 row_name   _col1   _col2
----------+------------+------------
  row1        val1         val2
  row2        val3         val4

 案例:

需求:在动态行转列不清楚生成的宽表有多少列的情况下,或者列太多,手动敲很不友好,那么可以借助创建函数(pivotcode)来简化工作:

创建函数pivotcode:

---pivotcode (tablename varchar, rowc varchar, colc varchar, cellc varchar, celldatatype varchar)
---pivotcode(tablename:要透视的源表的名称,rowc:要成为行的源表中的列的名称,colc:想成为列的源表中的列的名称,cellc:确定将如何创建单元格值的聚合表达式,celldatatype:单元格所需的数据类型)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

create or replace function pivotcode (tablename varchar, rowc varchar, colc varchar, cellc varchar, celldatatype varchar) returns varchar language plpgsql as $$
declare
    dynsql1 varchar;
    dynsql2 varchar;
    columnlist varchar;
begin
    -- 1. retrieve list of column names.
    dynsql1 = 'select string_agg(distinct ''_''||'||colc||'||'' '||celldatatype||''','','' order by ''_''||'||colc||'||'' '||celldatatype||''') from '||tablename||';';
    execute dynsql1 into columnlist;
    -- 2. set up the crosstab query
    dynsql2 = 'select * from crosstab (
 ''select '||rowc||','||colc||','||cellc||' from '||tablename||' group by 1,2 order by 1,2'',
 ''select distinct '||colc||' from '||tablename||' order by 1''
 )
 as newtable (
 '||rowc||' varchar,'||columnlist||'
 );';
    return dynsql2;
end
$$

创建表并插入数据:

create table table_to_pivot (
   rowname varchar,
   colname varchar,
   cellval numeric
);

insert into table_to_pivot values ('row1','col1',11);
insert into table_to_pivot values ('row1','col2',12);
insert into table_to_pivot values ('row1','col3',13);
insert into table_to_pivot values ('row2','col1',21);
insert into table_to_pivot values ('row2','col2',22);
insert into table_to_pivot values ('row2','col3',23);
insert into table_to_pivot values ('row3','col1',31);
insert into table_to_pivot values ('row3','col2',32);
insert into table_to_pivot values ('row3','col3',33);
insert into table_to_pivot values ('row1','col1',141);
insert into table_to_pivot values ('row1','col2',122);
insert into table_to_pivot values ('row4','col3',131);
insert into table_to_pivot values ('row1','col1',211);
insert into table_to_pivot values ('row3','col2',222);
insert into table_to_pivot values ('row5','col3',163);
insert into table_to_pivot values ('row7','col8',316);
insert into table_to_pivot values ('row5','col2',324);
insert into table_to_pivot values ('row33','col3',233);

 使用函数pivotcode:

select pivotcode('table_to_pivot','rowname','colname','max(cellval)','integer')

执行 pivotcode 生成的 SQL :

select * from crosstab (
 'select rowname,colname,max(cellval) from table_to_pivot group by 1,2 order by 1,2',
 'select distinct colname from table_to_pivot order by 1'
 )
 as newtable (
 rowname varchar,_col1 integer,_col2 integer,_col3 integer,_col8 integer
 );

 运行SQL得到透视表:

函数pivotcode,有以下缺点:

  • 如果返回的列有空格,需要自己手工修改

  • 可能会有大量的 Null 值,而不是 0

  • 返回的是一个 SQL,您需要复制出来后自己再执行

update版(实现pivotcode函数返回 json数据):

直接通过pivotcode函数返回结果,而不是返回一个待执行的 SQL 语句

-- PL/pgSQL code to create pivot tables with automatic column names
-- prerequisite: install the tablefunc module
create extension if not exists tablefunc;

-- tablename: name of source table you want to pivot
-- rowc: the name of the column in source table you want to be the rows
-- colc: the name of the column in source table you want to be the columns
-- cellc: an aggregate expression determining how the cell values will be created
-- celldatatype: desired data type for the cells
create or replace function pivot (tablename varchar, rowc varchar, colc varchar, cellc varchar, celldatatype varchar)
returns json language plpgsql as $$
declare
    dynsql1 varchar;
    dynsql2 varchar;
    columnlist varchar;
    stmt TEXT;
    result json;

begin
    -- 1. retrieve list of column names.
    dynsql1 = 'select string_agg(distinct ''_''||'||colc||'||'' '||celldatatype||''','','' order by ''_''||'||colc||'||'' '||celldatatype||''') from '||tablename||';';
    execute dynsql1 into columnlist;
    -- 2. set up the crosstab query
    dynsql2 = 'select * from crosstab (
 ''select '||rowc||','||colc||','||cellc||' from '||tablename||' group by 1,2 order by 1,2'',
 ''select distinct '||colc||' from '||tablename||' order by 1''
 )
 as newtable (
 '||rowc||' varchar,'||columnlist||'
 )';

stmt = E' select array_to_json(array_agg(row_to_json(t)))  from ('|| dynsql2 ||') t ';

execute stmt into result;
return result;
end
$$
select pivot('table_to_pivot','rowname','colname','max(cellval)','integer')

 
  json结果数据:

[
{"rowname":"row1","_col1":211,"_col2":122,"_col3":13,"_col8":null},

{"rowname":"row2","_col1":21,"_col2":22,"_col3":23,"_col8":null},

{"rowname":"row3","_col1":31,"_col2":222,"_col3":33,"_col8":null},

{"rowname":"row33","_col1":null,"_col2":null,"_col3":233,"_col8":null},

{"rowname":"row4","_col1":null,"_col2":null,"_col3":131,"_col8":null},

{"rowname":"row5","_col1":null,"_col2":324,"_col3":163,"_col8":null},

{"rowname":"row7","_col1":null,"_col2":null,"_col3":null,"_col8":316}
]

参考文献 : 

postgresql11:https://www.postgresql.org/docs/11/tablefunc.html

Pivot函数:  https://madlib.apache.org/docs/latest/group__grp__pivot.html

pivotmytable: https://github.com/jtornero/pivotmytable

crosstabview: https://wiki.postgresql.org/wiki/Crosstabview

dynamic_pivot: https://postgresql.verite.pro/blog/2018/06/19/crosstab-pivot.html

 

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PostgreSQL ,除了使用 crosstab() 函数进行行转列之外,还有其他方法可以实现这个操作。下面介绍两种常用的方法: 1. 使用 CASE 表达式: 可以使用 CASE 表达式将行转换为。首先,需要确定要将哪些行值作为名,然后使用 CASE 表达式为每个名创建一个条件。下面是一个示例: ```sql SELECT category, MAX(CASE WHEN attribute = 'Attribute1' THEN value END) AS attribute1, MAX(CASE WHEN attribute = 'Attribute2' THEN value END) AS attribute2 FROM data GROUP BY category; ``` 在上述示例,我们使用了 CASE 表达式来创建两个新的(attribute1 和 attribute2),并将相应的值填充到这些。 2. 使用 PostgreSQL 的透视表(Pivot Table)扩展: PostgreSQL 提供了一个名为 tablefunc 的扩展,其包含了 crosstab() 函数用于行转列的操作。除了 crosstab() 函数之外,tablefunc 还提供了一个重要的函数 called "pivot",它可以更方便地实现行转列的操作。 首先,确保已安装了 tablefunc 扩展: ```sql CREATE EXTENSION IF NOT EXISTS tablefunc; ``` 然后,可以使用 pivot 函数将行转换为。下面是一个示例: ```sql SELECT * FROM pivot( 'SELECT category, attribute, value FROM data ORDER BY 1', $$VALUES ('Attribute1'), ('Attribute2')$$ ) AS pt (category text, attribute1 text, attribute2 text); ``` 在上述示例,我们使用 pivot 函数将 attribute1 和 attribute2 转换为。 这些是在 PostgreSQL 除了 crosstab() 函数之外的两种常用方法来实现行转列的操作。根据具体的需求和数据结构,可以选择适合的方法来实现行转列

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值