`PostgreSQL` 实现交叉表查询(1)

PostgreSQL 8.3 版本开始,引入了 tablefunc扩展功能。它提供了很多有趣功能,其中就包括 tablefunc 函数,可以创建交叉表功能。

学习最佳方式就是从示例开始,我们首先初始化表和数据,然后创建交叉表。本文先实现简单交叉表,后续在增加更复杂功能。

创建示例表和数据

我们示例表是学生成绩表,包括学生姓名、科目、成绩、考试日期:

create table evaluations(
	stu_name varchar(60),
	subject varchar(60),
	eval_result numeric(3,1),
	eval_day    date
);

insert into evaluations(stu_name,subject,eval_result,eval_day) values
('smith',	'music',	7.0,	'2016-03-01'),
('smith',	'maths',	4.0,	'2016-03-01'),
('smith',	'history',	9.0,	'2016-03-22'),
('smith',	'chinese',	7.0,	'2016-03-15'),
('smith', 	'geography',	9.0,	'2016-03-04'),
('peter',	'music',	2.0,'2016-03-01'),
('peter',	'maths',	10.0,	'2016-03-01'),
('peter',	'history',	7.0,	'2016-03-22'),
('peter',	'chinese',	4.0,	'2016-03-15'),
('peter',	'geography',	10.0,	'2016-03-04')

-- select * from evaluations e 

展示结果方便理解:

stu_namesubjecteval_resulteval_day
smithmusic7.02016-04-01
smithmaths4.02016-04-01
smithhistory9.02016-03-22
smithchinese7.02016-03-15
smithgeography9.02016-03-04
petermusic2.02016-03-01
petermaths10.02016-03-01
peterhistory7.02016-03-22
peterchinese4.02016-03-15
petergeography10.02016-03-04

示例1:创建交叉表

下面表格非常直观,很容易看到学生的每科成绩,一般我们称为交叉表。仔细观察你会发现,它使用科目值作为列头(字段名称):

stu_namemusicmathshistorychinesegeography
peter4.010.07.010.02.0
smith7.09.09.04.07.0

安装 tablefunc 扩展

前面已经提到,交叉表是 tablefunc 扩展 功能的一部分。要使用交叉表,首先需要安装扩展:

CREATE extension tablefunc;

详解交叉表

crosstab 函数接收 sql 的select 语句作为参数,但必须服从下列限制:

  1. SELECT 必须返回3列
  2. SELECT 的第一列 必须是交叉表的行的标识列,在我们的示例中是学生名称。第二列表示交叉表的类别,我们示例中为科目,要特别注意,这列的值将在交叉表中被扩展为多列;如果返回5个不同值,交叉表将生成5列。
  3. 第三列将被赋给交叉表对应的单元格,我们示例是考试分数。

如果把交叉表比作二维数组,那么select第一列数组第一维,select第二列表示数组第二维,第三个值是数组元素值。如:grid[first_value][second_value]=third_value.

我们的SELECT 语句为:

select stu_name, subject, eval_result from evaluations e order by 1,2

crosstab 函数 需要在from 子句中使用,因此必须定义最终结果的列名和数据类型:

 as final_result(stu_name  varchar, music NUMERIC, maths NUMERIC, history NUMERIC, chinese NUMERIC, geography NUMERIC)

完整语句为:

select * from crosstab(
	'select stu_name, subject, eval_result from evaluations e order by 1,2'
) as final_result(stu_name  varchar, music NUMERIC, maths NUMERIC, history NUMERIC, chinese NUMERIC, geography NUMERIC)

结果如下:

stu_namemusicmathshistorychinesegeography
peter4.010.07.010.02.0
smith7.09.09.04.07.0

示例2 : 查找缺考学生记录

本节解决上节示例可能存在的问题。假设有下面场景:

我们想查询一些学生某些科目没有考试结果。可能你尝试下面查询代码如下:

SELECT * 
FROM crosstab( 'select stu_name, subject, eval_result from evaluations where extract (month from eval_day) = 3 order by 1,2') 
     AS final_result(Student varchar, Geography NUMERIC,History NUMERIC,Language NUMERIC,Maths NUMERIC,Music NUMERIC);

结果:

studentgeographyhistorylanguagemathsmusic
peter4.010.07.010.02.0
smith7.09.09.0

但如果只查询 smith 3月份科目成绩:

select stu_name, subject, eval_result from evaluations where extract (month from eval_day) = 3 and stu_name = 'smith' order by 1,2

结果为:

stu_namesubjecteval_result
smithchinese7.0
smithgeography9.0
smithhistory9.0

当然这个结果是正确的,仅显示了原始数据。但问题是在交叉表中一些类别丢失了。为了修复这个问题,crosstab提供了第二个参数。

SELECT * 
FROM crosstab( 'select stu_name, subject, eval_result from evaluations where extract (month from eval_day) = 3 and stu_name = ''smith'' order by 1,2',
	'select distinct subject from evaluations order by 1'
) AS final_result(Student varchar, Geography NUMERIC,History NUMERIC,Language NUMERIC,Maths NUMERIC,Music NUMERIC);

结果为:

studentgeographyhistorylanguagemathsmusic
smith7.09.09.0
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值