postgresql高级应用之行转列&汇总求和
postgresql高级应用之行转列&汇总求和
前言
节前公司业务方需要做一個統計報表,这个报表用于统计当月估计几个明星品的销售情况,而我们的数据是按行存储的就是日期|产品|渠道|销售额这样,说是也奇了怪了,我们买的报(guan)表(yuan)系(la)统(ji) 竟然不能容易地实现。。。,于是我看了看,然后想了想,发现是可以通过sql算出这样一个报表(多亏了postgresql的高阶函数),然后直接将数据输出到报表系统 完事兒~ ,以下 我將sql關鍵部分描述下,至於對前端展示有興趣的同學可留言,可考慮作一節講講哈~
报表
首先,業務需要的報表長這樣子的,看起來似乎還OK哈~

接下來我先給出我的測試脚本(均測試&無bug)~
表结构
drop table if EXISTS report1 ;
CREATE TABLE "report1" (
"id" numeric(22) NOT NULL,
"date" date NOT NULL,
"product" varchar(100),
"channel" varchar(100),
"amount" numeric(20,4)
);
表注释
| 字段 | 描述 |
|---|---|
| id | 主键 |
| date | 日期 |
| product | 产品 |
| channel | 渠道 |
| amount | 销售额 |
表数据
INSERT INTO "report1"("id", "date", "product", "channel", "amount") VALUES ('2105051726328010100000', '2021-05-04', '产品1', '京东', '8899.0000');
INSERT INTO "report1"("id", "date", "product", "channel", "amount") VALUES ('2105051726328010100001', '2021-05-04', '产品2', '京东', '99.0000');
INSERT INTO "report1"("id", "date", "product", "channel", "amount") VALUES ('2105051727068010100010', '2021-05-04', '产品1', '天猫', '230.0000');
INSERT INTO "report1"("id", "date", "product", "channel", "amount") VALUES ('2105051727068010100011', '2021-05-04', '产品2', '天猫', '9.9000');
INSERT INTO "report1"("id", "date", "product", "channel", "amount") VALUES ('2105051727068010100011', '2021-05-04', '产品3', '线下门店', '10.1000');
INSERT INTO "report1"("id", "date", "product", "channel", "amount") VALUES ('2105051727068010100000', '2021-05-04', '产品1', '其它', '10');
INSERT INTO "report1"("id", "date", "product", "channel", "amount") VALUES ('2105051727068010100099', '2021-05-04', '产品2', '其它', '20000');
INSERT INTO "report1"("id", "date", "product", "channel", "amount") VALUES ('2105051727068010100033', '2021-05-01', '产品1', '其它', '20000');
INSERT INTO "report1"("id", "date", "product", "channel", "amount") VALUES ('2105051727068010100044', '2021-05-01', '产品3', '线下门店', '12345');

思考
如果你看到這裏請稍稍思考下,一開篇我説過我們的數據是按 日期|产品|渠道|销售额 這樣按行存儲的,以上截圖大家一看就懂,然後再看看開篇的報表截圖,我想大家可以同我一樣可以分析出以下幾點:
報表縱向看大致分三部分
- 一部分是前一日產品銷售明細
- 然後一部分是前一日產品渠道產品合計
- 最後一部分是按渠道做的月統計
報表橫向看大致分兩部分
- 上半部分是渠道明細及合計(日和月)
- 最後一部分則是所有渠道的產品合計、日合計、月合計
好了,問題來了,如何做呢,我是這麽想的:首先要很清楚的是你的sql大致分兩大部分(兩個子查詢)
- 一部分是前一日的數據
- 另一部分則是月份匯總數據
最後需要將兩部分數據做聯表查詢,這樣太贊了,似乎完成了報表的80%,至於最後一行的求總,這裏先賣個關子哈~
第一部分數據(前一日的數據)
- 我想我們立馬能做的第一部分sql恐怕就是行專列吧(似乎這是最容易實現的)
select
channel,
sum(case product when '产品1' then amount end) as c1,
sum(case product when '产品2' then amount end) as c2,
sum(case product when '产品3' then amount end) as c3
from report1
group by channel ;

sql似乎沒什麽問題,但是我們少了一列,對那就是按渠道日合計,當然如果您對postgresql窗口函數熟悉的話,這裏實現的方式估計你已經猜到了(窗口over函數),上sql...
select
channel,
day_sum,
sum(case product when '产品1' then amount end) as c1,
sum(case product when '产品2' then amount end) as c2,
sum(case product when '产品3' then amount end) as c3
from
( select *,sum(amount) over (partition by channel) as day_sum from report1 where date=to_date('2021-05-04','yyyy-MM-dd') ) as t1
group by t1.channel ,t1.day_sum;

哈哈,上圖的day_sum估計大家很熟悉了吧,哈哈哈~
看來已經成功地完成了日數據部分,這裏可能的難點可能就兩點
- 一是使用聚合函數(
sum)+分組(group by)做行專列(當然postgresql也有其他很好用的行專列擴展,這裏就不介紹啦~) - 另一個是使用窗口函數(
over)對明細提前做按渠道的窗口匯總,這樣渠道日合計(行)的數據就有啦~
想想是不是很容易,接下來我們看看第二部分數據怎麽獲取~
第二部分數據(月份匯總數據)
月份匯總的數據看似簡單的可怕,如果您熟練掌握postgresql中的日期處理的話估計分分鐘就能搞定,這裏就不耍大刀了,直接放出sql,哈哈哈
select
channel,sum(amount) as month_sum from report1
where
date>=date(date_trunc('month',to_date('2021-05-04','yyyy-MM-dd'))) and date < date(date_trunc('month',to_date('2021-05-04','yyyy-MM-dd')) + '1 month')
group by
channel

報表數據最終求解
現在,我們將求解的兩部分數據按渠道channel字段做inner join合并以上兩部分數據,合并后的數據大致是這樣子的

這個是sql
select
ttt.channel,
sum(ttt.day_sum) as day_sum,
sum(ttt.month_sum) as month_sum,
sum(ttt.c1) as c1,
sum(ttt.c2) as c2,
sum(ttt.c3) as c3
from (
select tt1.*,tt2.month_sum from
(
select
channel,
day_sum,
sum(case product when '产品1' then amount end) as c1,
sum(case product when '产品2' then amount end) as c2,
sum(case product when '产品3' then amount end) as c3
from
( select *,sum(amount) over (partition by channel) as day_sum from report1 where date=to_date('2021-05-04','yyyy-MM-dd') ) as t1
group by t1.channel ,t1.day_sum
) as tt1 left join
(
select channel,sum(amount) as month_sum from report1 where date>=date(date_trunc('month',to_date('2021-05-04','yyyy-MM-dd'))) and date < date(date_trunc('month',to_date('2021-05-04','yyyy-MM-dd')) + '1 month') group by channel
) as tt2 on tt1.channel = tt2.channel
) ttt
GROUP BY ttt.channel
order by channel asc
看,匯總的數據已經有了,已經可以算作是最終結果了(如果你需要報表系統來計算匯總行數據的話),當然 ,我們的報表系統過於繁瑣(不是不能做,而是太麻煩),需要你將做好的菜喂給它吃,這時,該怎麽辦呢。。。,哈哈哈 我們似乎忘記了很久不用的rollup函數(一開始我也沒發現有這麽個函數哈哈),試試看吧
select
ttt.channel,
sum(ttt.day_sum) as day_sum,
sum(ttt.month_sum) as month_sum,
sum(ttt.c1) as c1,
sum(ttt.c2) as c2,
sum(ttt.c3) as c3
from (
select tt1.*,tt2.month_sum from
(
select
channel,
day_sum,
sum(case product when '产品1' then amount end) as c1,
sum(case product when '产品2' then amount end) as c2,
sum(case product when '产品3' then amount end) as c3
from
( select *,sum(amount) over (partition by channel) as day_sum from report1 where date=to_date('2021-05-04','yyyy-MM-dd') ) as t1
group by t1.channel ,t1.day_sum
) as tt1 left join
(
select channel,sum(amount) as month_sum from report1 where date>=date(date_trunc('month',to_date('2021-05-04','yyyy-MM-dd'))) and date < date(date_trunc('month',to_date('2021-05-04','yyyy-MM-dd')) + '1 month') group by channel
) as tt2 on tt1.channel = tt2.channel
) ttt
group by rollup(ttt.channel)
order by channel asc

數是對的,意味著我們成功了~
總結
如果您肯下功夫學,postgresql世界有很多精彩的東西,當然也有一些東西對比mysql顯得繁瑣些,不過本著學習的心態,我們縂能剋服這些,同時我們還是能做出超出我們自身能力範疇的東西的,哈哈,各位加油哦~
下章,我將講一講如何實現通過sql實現前端合并單元格的效果,是不是很神奇(我保證你全網搜不到), 希望不翻車,哈哈哈~
postgresql高级应用之行转列&汇总求和的更多相关文章
- SQL行转列汇总
PIVOT用于将列值旋转为列名(即行转列),在SQL Server 2000可以用聚合函数配合CASE语句实现 PIVOT的一般语法是:PIVOT(聚合函数(列) FOR 列 in (…) )AS P ...
- SQL行转列汇总 (转)
PIVOT 用于将列值旋转为列名(即行转列),在 SQL Server 2000可以用聚合函数配合CASE语句实现 PIVOT 的一般语法是:PIVOT(聚合函数(列) FOR 列 in (…) )A ...
- 转 SQL行转列汇总
1.PIVOT 用于将列值旋转为列名(即行转列) PIVOT 的一般语法是:PIVOT(聚合函数(列名) FOR 列名 in (列值1,…) )AS P select * from TB pivot ...
- postgresql高级应用之合并单元格
postgresql高级应用之合并单元格 转载请注明出处https://www.cnblogs.com/funnyzpc/p/14732172.html 1.写在前面✍ 继上一篇postgresql高 ...
- Ms sql行转列。汇总
SQL行转列汇总 PIVOT 用于将列值旋转为列名(即行转列),在 SQL Server 2000可以用聚合函数配合CASE语句实现 PIVOT 的一般语法是:PIVOT(聚合函数(列) FOR 列 ...
- Sql 语句收集——行转列
SQL行转列汇总 PIVOT用于将列值旋转为列名(即行转列),在SQL Server 2000可以用聚合函数配合CASE语句实现 PIVOT的一般语法是:PIVOT(聚合函数(列) FOR 列 in ...
- mysql 行转列 (结果集以坐标显示)
create table capacity( type int , numbers int , monthst INT ); select type, sum(case monthst when 1 ...
- postgresql行转列
问:怎么分页&&按条件&&按顺序&&姓名不重复查出数据? 答:其实就是行转列,那么,postgresql怎么进行转列呢,百度了下,大概有三种写法 写法1 ...
- 中等难度SQL语句(存储过程,分页,拼接字段、游标,日期类型转换,动态行转列,视图)汇总
一.创建存储过程 if Exists(select name from sysobjects where NAME = 'sp1LoginUser' and type='P')drop procedu ...
随机推荐
- 简单&&大数取模
Big Number Problem Description As we know, Big Number is always troublesome. But it's really importa ...
- 优化程序性能(CSAPP)
[前言]虽然现在没有接触过大型项目,但是工作了会注重性能.学习一下,应该能更好更快的理解别人写的经典优化的代码.结合CSAPP和自己的理解,总结一下. 一.程序优化综述 1.高效程序的特点 (1)适当 ...
- ServiceMesh
传统微服务架构 在微服务模式下,企业内部服务少则几个到几十个,多则上百个,每个服务一般都以集群方式部署,这时自然产生两个问题: 一.服务发现:服务的消费方(Consumer)如何发现服务的提供方(Pr ...
- es6 快速入门 —— 函数
其他章节请看: es6 快速入门 系列 函数 函数是所有编程语言的重要组成部分,es6之前函数语法一直没什么变化,遗留了许多问题,javaScript开发者多年来不断抱怨,es6终于决定大力度更新函数 ...
- MySQL注入与informantion_schema库
目录 只可读 自动开启 和MySQL注入有关的3个表 手动注入的使用案例 表介绍 查询一个表中全部字段的过程 MySQL V5.0安装完成会默认会生成一个库(informantion_schema), ...
- MySql学习---数据库基本类型,事务,多表查询
数据库分类 关系型数据库 行列, 列如Mysql,oracle 通过表和表之间,行和列之间的关系进行数据的存储 非关系型数据库: Redis,MongDb 以对象存储,同过对象的自身属性来决定 表与表 ...
- Java 面向对象 03
面向对象·三级 代码块的概述和分类 * A:代码块概述 * 在Java中,使用 { } 括起来的代码被称为代码块. * B:代码块分类 * 根据其位置和声明的不同,可以分为局部代码块, ...
- MyBatis(八):MyBatis插件机制详解
MyBatis插件插件机制简介 MyBatis插件其实就是为使用者提供的自行拓展拦截器,主要是为了可以更好的满足业务需要. 在MyBatis中提供了四大核心组件对数据库进行处理,分别是Exec ...
- 微信小程序Animation动画的使用
目录 1,前言 2,属性 3,使用 1,前言 和css3动画不同,小程序中动画是主要是通过js控制的,简单来说就是创建一个动画实例animation.调用实例的方法来定义动画效果.最后通过动画实例的e ...
- springMVC:校验框架:多规则校验,嵌套校验,分组校验;ssm整合技术
知识点梳理 课堂讲义 学习目标 能够阐述表单验证的分类和区别 能够运用表单验证的常用注解 能够编写表单验证的示例 能够编写SSM整合的应用案例 能够总结SSM整合的步骤 1 校验框架 1.1 入门-视 ...