PostgreSQL 实现交叉表查询(2)

上节我们学习使用 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 

每月平均成绩

我们通过 学生名称和月份进行分组:

select stu_name, extract (month from eval_day) eval_month, round(avg(eval_result),2) avg_result
from  evaluations
group by stu_name , extract (month from eval_day) 

显示结果:

stu_nameeval_monthavg_result
smith3.08.33
smith4.05.50
peter3.06.60

下面我们使用 实现交叉表查询:

select * from crosstab(
$$
select stu_name, extract (month from eval_day) eval_month, round(avg(eval_result),2) avg_result
from  evaluations
group by stu_name , extract (month from eval_day) 
order by 1,2
$$
) as avg_result(stu_name text, month3_avg numeric, month4_avg numeric)

执行返回错误:

SQL 错误 [42804]: 错误: invalid return type
  详细:SQL rowid datatype does not match return rowid datatype.

这是因为 crosstab 不能智能识别返回的月份信息,我们需要通过第二个参数进行显示设定,crosstab 完整语法如下:

crosstab(text source_sql, text category_sql)  

修改上面代码:

select * from crosstab(
$$
select stu_name, extract (month from eval_day) eval_month,round(avg(eval_result),2) avg_result
from  evaluations
group by stu_name , extract (month from eval_day) 
order by 1,2
$$
,'select distinct  extract (month from eval_day) eval_month from evaluations order by 1'
) as avg_result(stu_name text, month3_avg numeric, month4_avg numeric)

再次执行返回:

stu_namemonth3_avgmonth4_avg
peter6.60
smith8.335.50

与我们的预期一致。这时我们可能会想交叉表能不能同时显示每月的平均成绩和总成绩。

每月平均成绩与总成绩

上面已经完成了月度平均成绩的计算,同理计算月度总成绩就简单了:

select * from crosstab(
$$
select stu_name, extract (month from eval_day) eval_month,round(sum(eval_result),2) sum_result
from  evaluations
group by stu_name , extract (month from eval_day) 
order by 1,2
$$
,'select distinct  extract (month from eval_day) eval_month from evaluations order by 1'
) as avg_result(stu_name text, month3_sum numeric, month4_sum numeric)

执行结果:

stu_namemonth3_summonth4_sum
peter33.00
smith25.0011.00

既然两者都已经计算处理,那么我们通过with进行组合:

with avg_data as (
	select * from crosstab(
	$$
	select stu_name, extract (month from eval_day) eval_month,round(avg(eval_result),2) avg_result
	from  evaluations
	group by stu_name , extract (month from eval_day) 
	order by 1,2
	$$
	,'select distinct  extract (month from eval_day) eval_month from evaluations order by 1'
	) as avg_result(stu_name text, month3_avg numeric, month4_avg numeric)
),
sum_data as (
	select * from crosstab(
	$$
	select stu_name, extract (month from eval_day) eval_month,round(sum(eval_result),2) sum_result
	from  evaluations
	group by stu_name , extract (month from eval_day) 
	order by 1,2
	$$
	,'select distinct  extract (month from eval_day) eval_month from evaluations order by 1'
	) as avg_result(stu_name text, month3_sum numeric, month4_sum numeric)
)
select a.stu_name,a.month3_avg, a.month4_avg, month3_sum, month4_sum
from avg_data as a join sum_data as b
on a.stu_name = b.stu_name

执行结果:

stu_namemonth3_avgmonth4_avgmonth3_summonth4_sum
peter6.6033.00
smith8.335.5025.0011.00

漂亮,与我们预想的一致,只是代码稍微有点冗长,但又有一定相似性。未来我们继续进行优化。

总结

本文通过 tablefunc实现交叉表展示聚合函数的值,并利用with展示多个聚合函数的值,突破了 tablefunc功能限制。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值