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解析)的更多相关文章

  1. SQL查询表的行列转换/小计/统计(with rollup,with cube,pivot解析)

    SQL查询表的行列转换/小计/统计(with rollup,with cube,pivot解析) 2013-8-20 1.    SQL查询表的行列转换/小计/统计(with  rollup,with ...

  2. SQL Server中行列转换 Pivot UnPivot

    SQL Server中行列转换 Pivot UnPivot PIVOT用于将列值旋转为列名(即行转列),在SQL Server 2000可以用聚合函数配合CASE语句实现 PIVOT的一般语法是:PI ...

  3. sql server动态行列转换

    原文链接:https://www.cnblogs.com/gaizai/p/3753296.html sql server动态行列转换 一.本文所涉及的内容(Contents) 本文所涉及的内容(Co ...

  4. SQL Server 之 GROUP BY、GROUPING SETS、ROLLUP、CUBE

    1.创建表 Staff CREATE TABLE [dbo].[Staff]( ,) NOT NULL, ) NULL, ) NULL, ) NULL, [Money] [int] NULL, [Cr ...

  5. SQL实现数据行列转换

    前言: 在日常的工作中,使用数据库查看数据是很经常的事,数据库的数据非常多,如果此时的数据设计是一行行的设计话,就会有多行同一个用户的数据,查看起来比较费劲,如果数据较多时,不方便查看,为了更加方便工 ...

  6. 每日学习心得:UEditor样式被过滤无法显示问题

    前言: 上周开发中有用到开源的富文本编辑器UEditor,在使用的过程中遇到了样式被过滤无法显示问题,经过一番折腾终解决,此外,还有一些关于获取前台界面元素的一些总结. 1. UEditor样式被过滤 ...

  7. 每日学习心得:Js获取Checkboxlist所选值、instanceof 和typeof区别、为Array添加contains方法

    2013-11-24 前言: 上周在工作中遇到了一些跟JS以及前台交互的问题,虽然算不上多么高深,但是在解决时也走了一些弯路,所以就总结一下. 1.    JS获取checkboxList所选的值 这 ...

  8. 每日学习心得:Linq解决DataTable按照某一列的值排序问题/DataTable 导出CSV文件/巧用text-overflow解决数据绑定列数据展示过长问题

    2013-8-5 1 Linq解决DataTable按照某一列的值排序 在之前的总结中提到过对拼接而成的复合的DataTable按照某一列值的大小排序,那个主要的思想是在新建表结构时将要排序的那一列的 ...

  9. SQL Server中行列转换

    典型实例 一.行转列 1.建立表格 ifobject_id('tb')isnotnulldroptabletb go createtabletb(姓名varchar(10),课程varchar(10) ...

随机推荐

  1. EChart使用

    EChart ECharts,一个纯 Javascript 的图表库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等 ...

  2. 仿QQ大战—服务器的搭建(ServerSocket)

    ServerSocket(服务器): ServerSocket是JAVA中提供的用于建立服务器的类: 在客户/服务器通信模式中, 服务器端需要创建监听端口的 ServerSocket, ServerS ...

  3. ORBSLAM2与OPENCV3.1.0出错解决办法

    用opencv3.1.0做ORBSLAM2运行一下命令时cd ORB_SLAM2 chmod +x build.sh ./build.sh出错:/usr/bin/ld: CMakeFiles/mono ...

  4. Spark入门实战系列--6.SparkSQL(上)--SparkSQL简介

    [注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .SparkSQL的发展历程 1.1 Hive and Shark SparkSQL的前身是 ...

  5. Redhat=》中文

    我的redhat安装时没有提示语言选项,由于工程需要,支持汉字是不可避免的,因此就必须安装中文输入法. 安装中文包 将系统光盘镜像文件连接至计算机,我的镜像是RHEL5.1的,先将光盘挂载至/mnt目 ...

  6. sql server R2 下载地址收藏

    SQL Server 2008 R2 下载地址 32位: http://care.dlservice.microsoft.com/dl/download/1/e/6/1e626796-588a-495 ...

  7. 最全的TV视频应用合集,包含50多款客户端,有丰富直播点播

    这是我目前找到的 最好的视频应用合集,与坛友分享下.有50多款视频客户端,基本覆盖目前市面上口碑比较好的视频应用了. 里面有丰富的直播客户端,像 龙龙直播.泰捷直播.果子 Tv.More Tv等,还有 ...

  8. @synthesize 与@dynamic区别

    @synthesize 除非开发人员已经做了,否则由编译器自动生成getter/setter方法. 当开发人员自定义存或取方法时,自定义会屏蔽自动生成该方法. @dynamic 告诉编译器,不自动生成 ...

  9. ubuntu14.04 Markdown编辑器推荐之Remarkable

    如今已经习惯了用Markdown编辑器写博文的习惯,那么ubuntu以下有什么好用的呢?搜索中发现了这个叫Remarkable的免费Markdown编辑器.为什么推荐这个呢?说说它的特点: 实时预览 ...

  10. char 与 varchar 不同,造成的麻烦

    就是因为他们的不同,造成我一小天的麻烦,就是取得不了正确的结果,后来经原同事提醒,终于找到了原因,但是还有点没看懂,所以又找了个网上的经验,贴进来,以备以后再查. --简单的存储过程 create p ...