PostgreSQL 行列转换的用法

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云原生数据库 PolarDB 分布式版,标准版 2核8GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介:
一般在提交报表的时候,可能会使用到对数据表的行列变换的需求。
如以下数据:
digoal=> select * from tbl_sellers_info order by seller,se_year,se_month;
  seller  | se_year | se_month | se_amount 
----------+---------+----------+-----------
 卡洛斯   |    2011 |        3 |        12
 卡洛斯   |    2011 |        4 |        45
 卡洛斯   |    2011 |        5 |        56
 德哥     |    2010 |        1 |    123456
 德哥     |    2010 |        2 |    234567
 德哥     |    2010 |        3 |    345678
 德哥     |    2010 |        4 |    345678
 德哥     |    2010 |        5 |    567890
 德哥     |    2010 |        6 |    123456
 德哥     |    2010 |        7 |    234567
 德哥     |    2010 |        8 |    345678
 德哥     |    2010 |        9 |    345678
 德哥     |    2010 |       10 |    567890
 德哥     |    2010 |       11 |    123456
 德哥     |    2010 |       12 |    234567
 德哥     |    2011 |        1 |    123456
 德哥     |    2011 |        2 |    234567
 德哥     |    2011 |        3 |    345678
 德哥     |    2011 |        4 |    345678
 德哥     |    2011 |        5 |    567890
 罗纳尔多 |    2011 |        2 |        20
 罗纳尔多 |    2011 |        3 |        30
 罗纳尔多 |    2011 |        4 |        40
 罗纳尔多 |    2011 |        5 |        50
 贝克汉姆 |    2010 |       11 |        12
 贝克汉姆 |    2010 |       12 |        23
 贝克汉姆 |    2011 |        1 |        12
 贝克汉姆 |    2011 |        2 |        23
 贝克汉姆 |    2011 |        3 |        34
 贝克汉姆 |    2011 |        4 |        45
 贝克汉姆 |    2011 |        5 |        56
(31 rows)

 例如要按照每个SELLER出具每年的月度销售情况,可能要转换成:
图1 

在PostgreSQL9.1里面有一个比较好用的extension,tablefunc,可以轻松的完成行列转换。
下面会有例子来简单的描述以下这个extension.

1. 安装tablefunc extension.
\c digoal postgres
You are now connected to database "digoal" as user "postgres".
digoal=# create extension tablefunc;
CREATE EXTENSION
digoal=# \c digoal digoal
You are now connected to database "digoal" as user "digoal".
#安装完后会创建几个TYPE,几个函数.
type:
CREATE TYPE tablefunc_crosstab_N AS (     row_name TEXT,     category_1 TEXT,     category_2 TEXT,         .         .         .     category_N TEXT );
 public | tablefunc_crosstab_2 | tablefunc_crosstab_2 | tuple |          | 
 public | tablefunc_crosstab_3 | tablefunc_crosstab_3 | tuple |          | 
 public | tablefunc_crosstab_4 | tablefunc_crosstab_4 | tuple |          | 

function
normal_rand(int numvals, float8 mean, float8 stddev) returns setof float8
crosstab(text sql) crosstab(text sql, int N)
crosstabN(text sql)
crosstab(text source_sql, text category_sql)
connectby(text relname, text keyid_fld, text parent_keyid_fld           [, text orderby_fld ], text start_with, int max_depth           [, text branch_delim ])

2. 创建测试表
create table tbl_sellers_info (seller text,se_year int,se_month int,se_amount int);
insert into tbl_sellers_info values ('德哥',2011,01,123456);
insert into tbl_sellers_info values ('德哥',2011,02,234567);
insert into tbl_sellers_info values ('德哥',2011,03,345678);
insert into tbl_sellers_info values ('德哥',2011,04,345678);
insert into tbl_sellers_info values ('德哥',2011,05,567890);
insert into tbl_sellers_info values ('贝克汉姆',2011,01,12);
insert into tbl_sellers_info values ('贝克汉姆',2011,02,23);
insert into tbl_sellers_info values ('贝克汉姆',2011,03,34);
insert into tbl_sellers_info values ('贝克汉姆',2011,04,45);
insert into tbl_sellers_info values ('贝克汉姆',2011,05,56);
insert into tbl_sellers_info values ('卡洛斯',2011,03,12);
insert into tbl_sellers_info values ('卡洛斯',2011,04,45);
insert into tbl_sellers_info values ('卡洛斯',2011,05,56);
insert into tbl_sellers_info values ('罗纳尔多',2011,02,20);
insert into tbl_sellers_info values ('罗纳尔多',2011,03,30);
insert into tbl_sellers_info values ('罗纳尔多',2011,04,40);
insert into tbl_sellers_info values ('罗纳尔多',2011,05,50);
insert into tbl_sellers_info values ('德哥',2010,01,123456);
insert into tbl_sellers_info values ('德哥',2010,02,234567);
insert into tbl_sellers_info values ('德哥',2010,03,345678);
insert into tbl_sellers_info values ('德哥',2010,04,345678);
insert into tbl_sellers_info values ('德哥',2010,05,567890);
insert into tbl_sellers_info values ('德哥',2010,06,123456);
insert into tbl_sellers_info values ('德哥',2010,07,234567);
insert into tbl_sellers_info values ('德哥',2010,08,345678);
insert into tbl_sellers_info values ('德哥',2010,09,345678);
insert into tbl_sellers_info values ('德哥',2010,10,567890);
insert into tbl_sellers_info values ('德哥',2010,11,123456);
insert into tbl_sellers_info values ('德哥',2010,12,234567);
insert into tbl_sellers_info values ('贝克汉姆',2010,11,12);
insert into tbl_sellers_info values ('贝克汉姆',2010,12,23);

3. 下面我们用crosstab(text source_sql, text category_sql) 来满足需求.
select substr(seller,1,char_length(seller)-4) seller,substr(seller,char_length(seller)-3) se_year,jan  ,feb  ,mar  ,apr  ,may  ,jun  ,jul  ,aug  ,sep  ,oct  ,nov  ,dec from crosstab('select seller||se_year,se_month,se_amount from tbl_sellers_info order by 1', 
'select distinct se_month from tbl_sellers_info order by 1') as (seller text,Jan numeric,feb numeric,mar numeric,apr numeric,may numeric,jun numeric,jul numeric,aug numeric,sep numeric,oct numeric,nov numeric,dec numeric) order by 1,2;

# 分析一下这个函数:
参数一:(这个是需要进行行列变换的源SQL)
select seller||se_year,se_month,se_amount from tbl_sellers_info order by 1
参数二:(这个在这里代表的是月份,也就是se_month的值)
select distinct se_month from tbl_sellers_info order by 1
或者
select * from generate_series(1,12) order by 1
出来的结果都是一样的。
输出:
(seller text,Jan numeric,feb numeric,mar numeric,apr numeric,may numeric,jun numeric,jul numeric,aug numeric,sep numeric,oct numeric,nov numeric,dec numeric)
seller代表的是seller||se_year,这里必须这么做,因为需要按照这个来分组.
后面的就是月份了,数据类型是和se_amount一致就行了。

输出结果就是前面提到的图片。
行记录中没有的月份在变换后该月数据为空.例如卡洛斯只有2011年的3,4,5月份有数据,那么其他月份就是空白的。

# 另外一个要注意的是,输出结果的顺序是select * from generate_series(1,12) order by 1来决定的.
# 例如
select * from generate_series(1,12) order by 1 desc 那么输出的顺序就是反的,因此必须严格的排序.
图2 

# 另外的几个crosstab函数用法与此类似,只不过没有这个灵活。
# 而normal_rand用于返回一系列正态分布值。connect_by用于做异构查询(前面我有写过用with rescursive来实现)。就不再演示。
参考:
http://www.postgresql.org/docs/9.5/static/tablefunc.html

图1
938156097377363289
图2
3856770130890170969

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
SQL 关系型数据库 PostgreSQL
PostgreSQL datediff 日期间隔(单位转换)兼容SQL用法
标签 PostgreSQL , datediff 背景 使用datediff,对时间或日期相减,得到的间隔,转换为目标单位(日、月、季度、年、小时、秒。。。等)的数值。 DATEDIFF ( datepart, {date|timestamp}, {date|timestamp} ) 周...
15286 0
|
关系型数据库 PostgreSQL
|
关系型数据库 PostgreSQL
PostgreSQL listagg within group (order by) 聚合兼容用法 string_agg ( order by) - 行列变换,CSV构造...
标签 PostgreSQL , order-set agg , listagg , string_agg , order 背景 listagg — Rows to Delimited Strings The listagg function transforms values from a g...
6208 0
|
存储 SQL 监控
16PostgreSQL 本地分区表的用法和优化|学习笔记
快速学习16PostgreSQL 本地分区表的用法和优化
886 0
16PostgreSQL 本地分区表的用法和优化|学习笔记
|
SQL 安全 关系型数据库
17PostgreSQL shared nothing分布式用法讲解|学习笔记(三)
快速学习17PostgreSQL shared nothing分布式用法讲解
266 0
17PostgreSQL shared nothing分布式用法讲解|学习笔记(三)
|
SQL 存储 移动开发
PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(四)|学习笔记
快速学习3 PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(四)
 PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(四)|学习笔记
|
SQL JSON 关系型数据库
postgresql 高级用法
postgresql 高级用法
|
SQL 关系型数据库 数据库
3 PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(三)|学习笔记
快速学习3 PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(三)
|
SQL XML 移动开发
【学习资料】第14期快速入门PostgreSQL应用开发与管理 - 4 高级SQL用法
大家好,这里是快速入门PostgreSQL应用开发与管理 - 4 高级SQL用法
|
关系型数据库 PostgreSQL
postgresql行列转换函数
postgresql行列转换函数 Pg提供相关行列转换函数string_agg和regexp_split_to_table。行转列:string_agg 测试表 postgres=# select * from test.
6315 0

相关产品

  • 云原生数据库 PolarDB
  • 云数据库 RDS PostgreSQL 版
  • 下一篇
    无影云桌面