SQL查询表的行列转换/小计/统计(with rollup,with cube,pivot解析)
SQL查询表的行列转换/小计/统计(with rollup,with cube,pivot解析)
2013-8-20
1. SQL查询表的行列转换/小计/统计(with rollup,with cube,pivot解析)
在实际的项目开发中有很多项目都会有报表模块,今天就通过一个小的SQL查询统计来讲解一下实际开发中比较常用的行列转换/小计/统计等报表统计相关的常用知识点。
题目如下:
查询sales 和stores表,得出1993年每个store每季度销售数量及小计和总计,查询出的结果如下
其中sales表的数据结构如下:
其中stores表的数据结构如下:
1.1 普通方法(容易理解)
初看题目,第一感觉是竖表转横表,首先想到的是使用case when,
所以
第一步操作如下:
select st.stor_name,SUM(sa.qty) as Total, ( case when datepart(qq,sa.ord_date)=1 then SUM(sa.qty) else 0 end) as Qtr1, ( case when datepart(qq,sa.ord_date)=2 then SUM(sa.qty) else 0 end) as Qtr2, ( case when datepart(qq,sa.ord_date)=3 then SUM(sa.qty) else 0 end) as Qtr3, ( case when datepart(qq,sa.ord_date)=4 then SUM(sa.qty) else 0 end) as Qtr4 from stores st left join sales sa on st.stor_id=sa.stor_id where DATEPART(yy,sa.ord_date)=1993 group by st.stor_name,sa.ord_date |
检索出结果如下:
这个时候由检索的结果可知,其中部分商店的统计信息没有合并统计,原因在于分组的时候我们是按商店名和日期分组的,
第二步操作,将第一步检索的信息,再次按店名分组统计,sql语句如下:
select A.stor_name as stor_name ,SUM(A.Total) as Total,SUM(A.Qtr1) as Qtr1, SUM(A.Qtr2) as Qtr2,SUM(A.Qtr3) as Qtr3,SUM(A.Qtr4) as Qtr4 from ( --按时间和stor_name分组统计出对应的stor一年的销售明细 select st.stor_name,SUM(sa.qty) as Total, ( case when datepart(qq,sa.ord_date)=1 then SUM(sa.qty) else 0 end) as Qtr1, ( case when datepart(qq,sa.ord_date)=2 then SUM(sa.qty) else 0 end) as Qtr2, ( case when datepart(qq,sa.ord_date)=3 then SUM(sa.qty) else 0 end) as Qtr3, ( case when datepart(qq,sa.ord_date)=4 then SUM(sa.qty) else 0 end) as Qtr4 from stores st left join sales sa on st.stor_id=sa.stor_id where DATEPART(yy,sa.ord_date)=1993 group by st.stor_name,sa.ord_date) as A group by A.stor_name |
统计结果如下:
这个时候已经很接近标准答案了,但是还有一个统计行需要统计列出
第三步,将第二步统计的结果再和总计的结果Union一下就可以实现标准的结果
--对每个stor一年的销售明细进行汇总,之后按stor名分组
select A.stor_name as stor_name ,SUM(A.Total) as Total,SUM(A.Qtr1) as Qtr1, SUM(A.Qtr2) as Qtr2,SUM(A.Qtr3) as Qtr3,SUM(A.Qtr4) as Qtr4 from ( --按时间和stor_name分组统计出对应的stor一年的销售明细 select st.stor_name,SUM(sa.qty) as Total, ( case when datepart(qq,sa.ord_date)=1 then SUM(sa.qty) else 0 end) as Qtr1, ( case when datepart(qq,sa.ord_date)=2 then SUM(sa.qty) else 0 end) as Qtr2, ( case when datepart(qq,sa.ord_date)=3 then SUM(sa.qty) else 0 end) as Qtr3, ( case when datepart(qq,sa.ord_date)=4 then SUM(sa.qty) else 0 end) as Qtr4 from stores st left join sales sa on st.stor_id=sa.stor_id where DATEPART(yy,sa.ord_date)=1993 group by st.stor_name,sa.ord_date) as A group by A.stor_name union --汇总统计信息 select 'Total' ,SUM(Total),SUM(Qtr1),SUM(Qtr2),SUM(Qtr3),SUM(Qtr4) from ( --每个store一年的销售明细 select A.stor_name as stor_name ,SUM(A.Total) as Total,SUM(A.Qtr1) as Qtr1, SUM(A.Qtr2) as Qtr2,SUM(A.Qtr3) as Qtr3,SUM(A.Qtr4) as Qtr4 from ( select st.stor_name,SUM(sa.qty) as Total, ( case when datepart(qq,sa.ord_date)=1 then SUM(sa.qty) else 0 end) as Qtr1, ( case when datepart(qq,sa.ord_date)=2 then SUM(sa.qty) else 0 end) as Qtr2, ( case when datepart(qq,sa.ord_date)=3 then SUM(sa.qty) else 0 end) as Qtr3, ( case when datepart(qq,sa.ord_date)=4 then SUM(sa.qty) else 0 end) as Qtr4 from stores st left join sales sa on st.stor_id=sa.stor_id where DATEPART(yy,sa.ord_date)=1993 group by st.stor_name,sa.ord_date) as A group by A.stor_name ) as B |
执行之后就可以得出我们想要的结果。
总结一下解题的整个思路,首先看题目要求求出每个店铺每年,每季度的销售统计,同时最后还要有总计行,统计全年/每个季度的销售总额。
接着通过case when语句查询出每个商店每年每季度的销售总统计,因为是按商店名和时间分组的,所以在查询出大体的数据结构之后,还需要再对结果进行按商店分组统计,这样就统计出了符合答案要求的数据,最后在将统计出的结果与以结果为基础的再次统计union一下就得出了最终的答案。看起来很复杂的一个查询,只要把思路理清之后一步一步实现就很容易了。
虽然我们经过查询实现了题目的要求,但是再让我们回过头来看看我们的查询语句,数据少的时候这样查询还没什么问题,但是如果数据量过大就会有很严重的性能问题,同时,这样的sql查询语句过于庞大,有木有可以优化的方案呢?答案是肯定的。下面就给大家讲一下优化的查询解决方案。
1.2 With rollup + case when count
首先我们的查询思路还是一下的,先用case when语句构建出大体的查询框架,唯一不同的是在group by 之后我们多了with rollup语句。代码如下:
SELECT ISNULL(stor_name, 'Total' ) AS stor_name,SUM(qty) AS Total, SUM(CASE WHEN DATEPART(qq,ord_date)=1 THEN qty ELSE 0 END) AS Qtr1, SUM(CASE WHEN DATEPART(qq,ord_date)=2 THEN qty ELSE 0 END) AS Qtr2, SUM(CASE WHEN DATEPART(qq,ord_date)=3 THEN qty ELSE 0 END) AS Qtr3, SUM(CASE WHEN DATEPART(qq,ord_date)=4 THEN qty ELSE 0 END) AS Qtr4 FROM stores t INNER JOIN sales s ON s.stor_id = t.stor_id WHERE YEAR(s.ord_date) = '1993' GROUP BY stor_name WITH ROLLUP |
在group by 之后加上with rollup,我们执行一下查询语句,就会发现马上出现了我们想要的结果,这是为什么呢?
在生成包含小计和合计的报表时,ROLLUP 运算符很有用。GROUP BY子句允许一个将额外行添加到简略输出端 WITH ROLLUP 修饰符。这些行代表高层(或高聚集)简略操作。ROLLUP 因而允许你在多层分析的角度回答有关问询的问题。或者你可以使用 ROLLUP, 它能用一个问询提供双层分析。将一个 WITH ROLLUP修饰符添加到GROUP BY 语句,使询问产生另一行结果,也就是在上例中采用rollup之后,在按stor_name分组之后,还能检索出本组类的整体聚合信息。
如果有多重分组列的情况时,ROLLUP产生的效果更加复杂。这时,每次在除了最后一个分类列之外的任何列出现一个 “break” (值的改变) ,则问讯会产生一个高聚集累计行。
1.3 With cube + povit
上例中我们讲了使用with rullup来实现统计分组,那么还木有比with rollup 更加简便的查询呢?答案是肯定的。
首先我们想按照商店和时间分组统计出每家商店每年/季度的销售情况,这个时候我们需要借助于with cube语句。代码如下:
select isnull(t.stor_name, 'Total' ) as 'stor_name' , isnull(datepart(qq, ord_date),0) as 'Qtr' , sum(qty) as 'qty' from sales s join stores t on s.stor_id = t.stor_id where year(s.ord_date) = 1993 group by datepart(qq, ord_date), t.stor_name with cube |
执行结果如下:
With cube语句跟with rollup语句作用很相像,它们的区别在于with CUBE 生成的结果集显示了所选列中值的所有组合的聚合,而with ROLLUP 生成的结果集显示了所选列中值的某一层次结构的聚合
第二步,我们将原始数据经过第一步的查询之后转换成了个标准的竖表,下边要做的就是如何将这个竖表转换成横表,我们在上边都是用case when的语法来实现这种表的横竖转换,这里我们换一种方式来实现。这里我们用povit方法来实现。代码如下:
select stor_name, isnull([0],0) as 'Total' , isnull([1],0) as 'Qtr1' ,isnull([2],0) as 'Qtr2' , isnull([3],0) as 'Qtr3' , isnull([4],0) as 'Qtr4' from ( select isnull(t.stor_name, 'Total' ) as 'stor_name' , isnull(datepart(qq, ord_date),0) as 'Qtr' , sum(qty) as 'qty' from sales s join stores t on s.stor_id = t.stor_id where year(s.ord_date) = 1993 group by datepart(qq, ord_date), t.stor_name with cube ) as tmp pivot ( sum(qty) for Qtr in ([0], [1], [2], [3], [4]) ) as pvt |
上边代码示例中高亮部分即为使用pivot进行表的横竖转换的关键代码。
PIVOT用于行转列,在SQL Server 2000可以用聚合函数配合CASE语句实现,
PIVOT的一般语法是:PIVOT(聚合函数(列) FOR 列 in (…) )AS P
这跟我们上边示例中使用的高亮标注的部分的方法是一样的
总结:
通过这样一个简单的查询,引出了今天要讲的表的行列转换(case when 和 pivot两种方法),表数据的统计(with rollup 和with cube方法),这也就达到了总结的目的。重要的不是讲这些方法怎么怎么用,主要是讲求解决问题的一个思路,以及在解决问题后对性能及效率的优化,希望可以对大家有些帮助。
SQL查询表的行列转换/小计/统计(with rollup,with cube,pivot解析)的更多相关文章
- 每日学习心得:SQL查询表的行列转换/小计/统计(with rollup,with cube,pivot解析)
2013-8-20 1. SQL查询表的行列转换/小计/统计(with rollup,with cube,pivot解析) 在实际的项目开发中有很多项目都会有报表模块,今天就通过一个小的SQL ...
- SQL Server中行列转换 Pivot UnPivot
SQL Server中行列转换 Pivot UnPivot PIVOT用于将列值旋转为列名(即行转列),在SQL Server 2000可以用聚合函数配合CASE语句实现 PIVOT的一般语法是:PI ...
- sql server动态行列转换
原文链接:https://www.cnblogs.com/gaizai/p/3753296.html sql server动态行列转换 一.本文所涉及的内容(Contents) 本文所涉及的内容(Co ...
- SQL Server 之 GROUP BY、GROUPING SETS、ROLLUP、CUBE
1.创建表 Staff CREATE TABLE [dbo].[Staff]( ,) NOT NULL, ) NULL, ) NULL, ) NULL, [Money] [int] NULL, [Cr ...
- SQL实现数据行列转换
前言: 在日常的工作中,使用数据库查看数据是很经常的事,数据库的数据非常多,如果此时的数据设计是一行行的设计话,就会有多行同一个用户的数据,查看起来比较费劲,如果数据较多时,不方便查看,为了更加方便工 ...
- SQL Server中行列转换
典型实例 一.行转列 1.建立表格 ifobject_id('tb')isnotnulldroptabletb go createtabletb(姓名varchar(10),课程varchar(10) ...
- SQL技巧之行列转换
比如:id 姓名 状态 1 刘德华 12 刘德华 23 周华健 04 吴彦祖 1 在access中,用一条sql查询 ...
- SqlServer和Oracle中一些常用的sql语句3 行列转换
--217, SQL SERVER SELECT Cust_Name , MAX(CASE WHEN Order_Date ='2009-08-01' THEN AR END) "2009- ...
- oracle里面用sql做报表并带小计合计常用到的函数
1-- DECODE函数是Oracle PL/SQL是功能强大的函数之一,假设我们想给职员加工资,其标准是:工资在8000元以下的将加20%:工资在8000元以上的加15%,通常的做法是,先选出记录 ...
随机推荐
- SSAS系列——【06】多维数据(创建Cube)
原文:SSAS系列--[06]多维数据(创建Cube) 1.文件类型说明 项目定义文件 (.dwproj).项目用户设置 (.dwproj.user).数据源文件 (.ds).数据源视图文件 (.ds ...
- 动态创建一些常的html标签
原文:动态创建一些常的html标签 一段时间来,不管是在学习还是应用asp.net mvc应用程序,较多情况之下,需要动态创建一些html标签.如这篇<文本框下面有两个铵钮,点就加点减就减> ...
- 条形码(JBarcode)
一世尘梦 少小离家老大回,妖娆尘世,程序唧唧:问君能有几多愁,恰是满屏BUG无处修. 商品条形码(JBarcode) 之前没有使用过这个,现在使用JBarcode生成商品条形码,工作之前的准备工作: ...
- css mainDiv和popbox居中
<style> .beCenter { width:460px; height:212px; background:#ccc; ...
- Linux C语言操作MySQL
原文:Linux C语言操作MySQL 1.MySQL数据库简介 MySQL是一个开源码的小型关系数据库管理系统,体积小,速度快,总体成本低,开源.MySQL有以下特性: (1) 使用C和C++编写, ...
- JavaEE——Intellij Idea 创建JavaWeb项目
原文:JavaEE--Intellij Idea 创建JavaWeb项目 折腾Tomcat折腾了两个晚上,第一个晚上怎么都进不了Tomcat的首页,第二个晚上进去了,但是新建的Web项目,在浏览器中运 ...
- VMware Workstation 无法与 Windows XP \ Windows 7 \ Windows 8 进行共享文件夹。
1.这是一个小Bug,做法很简单. ----①.如果安装了VMware Tools,先卸载VMware Tools,重启虚拟机,再安装VMware Tools,重启虚拟机,就行了. ----②.如果没 ...
- WimMaker 2.0 (2013.10) WIM制作工具
WimMaker 2.0 (2013.10) WIM制作工具 可用于制作PE启动内核的Wim文件 说明: 因本软件使用.NET2.0制作,故主要用于制作WIM映像不用于备份还原系统(虽可用,但不专业, ...
- C#异步Socket示例
C#异步Socket示例 概要 在C#领域或者说.net通信领域中有着众多的解决方案,WCF,HttpRequest,WebAPI,Remoting,socket等技术.这些技术都有着自己擅长的领域, ...
- Spring之单元测试
引言 是否在程序运行时使用单元测试是衡量一个程序员素质的一个重要指标.使用单元测试既可以让我检查程序逻辑的正确性还可以让我们减少程序测试的BUG,便于调试可以提高我们写程序的效率.以前我们做单元测试的 ...