服务器之家

服务器之家 > 正文

PostgreSQL实现交叉表(行列转换)的5种方法示例

时间:2020-05-01 17:22     来源/作者:Hason_Huang

交叉表

交叉表(Cross Tabulations)是一种常用的分类汇总表格。使用交叉表查询,显示源于表中某个字段的汇总值,并将它们分组,其中一组列在数据表的左侧,另一组列在数据表的上部。行和列的交叉处可以对数据进行多种汇总计算,如:求和、平均值、记数、最大值、最小值等。使用交叉表查询数据非常直观明了,被广泛应用。交叉表查询也是数据库的一个特点。

例如:

  select 表1.组名,
  (select 表1.成员姓名 from 表2 b where 表1.成员1id=表2.成员id) as 成员1id,
  (select 表1.成员姓名 from 表2 b where 表1.成员2id=表2.成员id) as 成员2id,
  (select 表1.成员姓名 from 表2 b where 表1.成员3id=表2.成员id) as 成员3id
  from 表1,表2
  --这种就是交叉表查询

交叉报表是报表当中常见的类型,属于基本的报表,是行、列方向都有分组的报表。这里牵涉到另外一个概念即分组报表。这是所有报表当中最普通,最常见的报表类型,也是所有报表工具都支持的一种报表格式。从一般概念上来讲,分组报表就是只有纵向的分组。传统的分组报表制作方式是把报表划分为条带状,用户根据一个数据绑定向导指定分组,汇总字段,生成标准的分组报表。

这里我来演示下在POSTGRESQL里面如何实现交叉表的展示,下面话不多说了,来一起看看详细的介绍吧

原始表数据如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
t_girl=# select * from score;
 name | subject | score
-------+---------+-------
 Lucy | English | 100
 Lucy | Physics | 90
 Lucy | Math | 85
 Lily | English | 95
 Lily | Physics | 81
 Lily | Math | 84
 David | English | 100
 David | Physics | 86
 David | Math | 89
 Simon | English | 90
 Simon | Physics | 76
 Simon | Math | 79
(12 rows)
 
 
Time: 2.066 ms

想要实现以下的结果:

?
1
2
3
4
5
6
name | English | Physics | Math
------+---------+---------+------
Simon |  90 |  76 | 79
Lucy |  100 |  90 | 85
Lily |  95 |  81 | 84
David |  100 |  86 | 89

大致有以下几种方法:

1、用标准SQL展现出来

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
t_girl=# select name,
t_girl-# sum(case when subject = 'English' then score else 0 end) as "English",
t_girl-# sum(case when subject = 'Physics' then score else 0 end) as "Physics",
t_girl-# sum(case when subject = 'Math' then score else 0 end) as "Math"
t_girl-# from score
t_girl-# group by name order by name desc;
 name | English | Physics | Math
-------+---------+---------+------
 Simon |  90 |  76 | 79
 Lucy |  100 |  90 | 85
 Lily |  95 |  81 | 84
 David |  100 |  86 | 89
(4 rows)
 
 
Time: 1.123 ms

2、用PostgreSQL 提供的第三方扩展 tablefunc 带来的函数实现

以下函数crosstab 里面的SQL必须有三个字段,name, 分类以及分类值来作为起始参数,必须以name,分类值作为输出参数。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
t_girl=# SELECT *
FROM crosstab('select name,subject,score from score order by name desc',$$values ('English'::text),('Physics'::text),('Math'::text)$$)
AS score(name text, English int, Physics int, Math int);
 name | english | physics | math
-------+---------+---------+------
 Simon |  90 |  76 | 79
 Lucy |  100 |  90 | 85
 Lily |  95 |  81 | 84
 David |  100 |  86 | 89
(4 rows)
 
 
Time: 2.059 ms

3、用PostgreSQL 自身的聚合函数实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
t_girl=# select name,split_part(split_part(tmp,',',1),':',2) as "English",
t_girl-# split_part(split_part(tmp,',',2),':',2) as "Physics",
t_girl-# split_part(split_part(tmp,',',3),':',2) as "Math"
t_girl-# from
t_girl-# (
t_girl(# select name,string_agg(subject||':'||score,',') as tmp from score group by name order by name desc
t_girl(# ) as T;
 name | English | Physics | Math
-------+---------+---------+------
 Simon | 90  | 76  | 79
 Lucy | 100  | 90  | 85
 Lily | 95  | 81  | 84
 David | 100  | 86  | 89
(4 rows)
 
 
Time: 2.396 ms

4、 存储函数实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
create or replace function func_ytt_crosstab_py ()
returns setof ytt_crosstab
as
$ytt$
 for row in plpy.cursor("select name,string_agg(subject||':'||score,',') as tmp from score group by name order by name desc"):
  a = row['tmp'].split(',')
  yield (row['name'],a[0].split(':')[1],a[1].split(':')[1],a[2].split(':')[1])
$ytt$ language plpythonu;
 
 
t_girl=# select name,english,physics,math from func_ytt_crosstab_py();
 name | english | physics | math
-------+---------+---------+------
 Simon | 90  | 76  | 79
 Lucy | 100  | 90  | 85
 Lily | 95  | 81  | 84
 David | 100  | 86  | 89
(4 rows)
 
 
Time: 2.687 ms

5、 用PLPGSQL来实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
t_girl=# create type ytt_crosstab as (name text, English text, Physics text, Math text);
CREATE TYPE
Time: 22.518 ms
 
 
create or replace function func_ytt_crosstab ()
returns setof ytt_crosstab
as
$ytt$
 declare v_name text := '';
    v_english text := '';
  v_physics text := '';
  v_math text := '';
  v_tmp_result text := '';
 declare cs1 cursor for select name,string_agg(subject||':'||score,',') from score group by name order by name desc;
begin
 open cs1;
 loop
 fetch cs1 into v_name,v_tmp_result;
 exit when not found;
 v_english = split_part(split_part(v_tmp_result,',',1),':',2);
 v_physics = split_part(split_part(v_tmp_result,',',2),':',2);
 v_math = split_part(split_part(v_tmp_result,',',3),':',2);
 return query select v_name,v_english,v_physics,v_math;
 end loop;
end;
$ytt$ language plpgsql;
 
 
t_girl=# select name,English,Physics,Math from func_ytt_crosstab();
 name | english | physics | math
-------+---------+---------+------
 Simon | 90  | 76  | 79
 Lucy | 100  | 90  | 85
 Lily | 95  | 81  | 84
 David | 100  | 86  | 89
(4 rows)
 
 
Time: 2.127 ms

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。

原文链接:https://blog.csdn.net/a258831020/article/details/48446213

相关文章

热门资讯

沙雕群名称大全2019精选 今年最火的微信群名沙雕有创意
沙雕群名称大全2019精选 今年最火的微信群名沙雕有创意 2019-07-07
玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分
玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分 2019-06-21
男生常说24816是什么意思?女生说13579是什么意思?
男生常说24816是什么意思?女生说13579是什么意思? 2019-09-17
超A是什么意思 你好a表达的是什么
超A是什么意思 你好a表达的是什么 2019-06-06
和平精英超高音质怎么修改 和平精英超高音质重启设置方法
和平精英超高音质怎么修改 和平精英超高音质重启设置方法 2019-06-26
返回顶部