你真的会玩SQL吗?系列目录

你真的会玩SQL吗?之逻辑查询处理阶段

你真的会玩SQL吗?和平大使 内连接、外连接

你真的会玩SQL吗?三范式、数据完整性

你真的会玩SQL吗?查询指定节点及其所有父节点的方法

你真的会玩SQL吗?让人晕头转向的三值逻辑

你真的会玩SQL吗?EXISTS和IN之间的区别

你真的会玩SQL吗?无处不在的子查询

你真的会玩SQL吗?Case也疯狂

你真的会玩SQL吗?表表达式,排名函数

你真的会玩SQL吗?简单的 数据修改

你真的会玩SQL吗?你所不知道的 数据聚合

你真的会玩SQL吗?透视转换的艺术

你真的会玩SQL吗?冷落的Top和Apply

你真的会玩SQL吗?实用函数方法汇总

你真的会玩SQL吗?玩爆你的数据报表之存储过程编写(上)

   你真的会玩SQL吗?玩爆你的数据报表之存储过程编写(下)

本系列之前的所有知识均为本章作准备,若看不懂本章可先回头温习下之前的系列。在之前还是先提一下中心思想:SQL数据处理是集合思维,不要用逻辑思维来思考。

在项目中经常需要从基础数据中提取数据进行处理后显示给老板或客户一些报表,这时数据量大,涉及表多,简单的表处理SQL无法满足,且需要重复使用,这时就要使用存储过程来处理大数据和复杂的业务逻辑。可能会有人提出在后台读出DataSet加载到内在中用逻辑来处理,但之前说过,逻辑处理远远没有数据库集合处理快,且占用了宝贵的内存,运用好可以减少网络流量、可提高数据库系统的安全性。

存储过程的编写最重要的是思路清晰,能知道自己想要的结果和写出的SQL能运行出什么样的结果,这需要基本功非常扎实,过程中会用到联表查询、更新、临时表、数据聚合、行列转换、简单的函数……等知识。

接下来不多说,直接上需求实例:

需求:统计某个项目下各个产品的具体销售情况

数据表:

表结构如下:

由于数据库数据经过翻倍,数据库用例数据数量有点大,请要下载的可以在此下载,然后自行还原数据库,传送门:链接:https://pan.baidu.com/s/1PIL2WC1Lks2aLcXvlEjPcg 密码:pfgl

需要经过编写SQL显示数据库中销售记录是按每个产品、每月一条记录存储的,需要展示如下图。

总的显示一个项目,然后再按每个产品进行分组展示,每个产品有7个属性行统计数据再将所有产品分别进行合计,放到各自产品上面

部分业务名词解释:

横项 总项目数据= 产品1 + 产品2 + 产品3 + 产品4……

      “项目合计”列:整个项目的,即以前年度合计+2011年合计+以后年度合计。如 产品1的“销售套数”的项目合计5555 = 2011 前年度合计3030 + 指定查询年2011年的505 + 2011年以后的2020 ,销售面积等以此类推。

“以前年度合计”列:2011年以前所有年的合计。

“以后年度合计”列:2011年以后所有年的合计。

“累计”,统计所在周期+以前合计,如2011-01累计销售面积指2011-01年以前(包括2011-01)的销售面积之和。

“累计销售比例”:累计销售面积/项目总销售面积。如 产品1 的2011-02的“累计销售面积比例”0.55 = 2011-02的“累计销售面积” 127200.00 / 产品1的项目合计的 “累计销售面积” 229900.00

  “累计销售面积”:到当前统计时间为止的所有销售面积,如产品1的2011-02月的“累计销售面积”127200.00 = 2011-1月"累计销售的面积"126400.00 + 2011-02月的“销售面积“ 800.00,其它概念以此类推。

在这里有个特别的是 “累计销售面积”的”以后年度合计“,如产品1的 2011年的 ”以后年度合计“ 的 “累计销售面积”229900.00 = 2011年的  “累计销售面积”146300.00 + 2011年后的 ”销售面积” 83600.00 ,在这里你会发现229900和产品1的项目合计的“累计销售面积”相同,这个是正确的,项目合计中的累积面积并不等于 以前年+当年+以后年,请理解一下这个滑动聚合概念。

其中需要传入两个参数:项目ID年份

  下面来理一理整体的思路:

如果只统计一个产品显示以上的数据该如何写呢?你可以先试一下。

先将数据表拆分:

横向:总项目合计+ 每个产品中每个子项(如 销售套数,销售面积等)+每个产品累计销售面积

竖向:项目、产品基本信息+当年每月各项累积+以前年度合计+以后年度合计+项目总合计

核心数据表:销售明细表,进行分析核心数据列:销售面积、销售均价、销售金额

核心操作:行、列互转,滑动聚合统计

   由于涉及到的知识过于庞大,流程过于繁多,导致整个篇幅过长,因此在这里分为上、下篇来讲解。

  那来看看整个流程思路,先过滤数据:

    1. 查找该项目的所有产品放进临时表A
    2. 查找该项目的所有产品的销售明细放进临时表B
    3. 从临时表B中查找指定年的销售明细放进临时表C
    4. 从表C统计当前年度合计列,各产品的所有面积、金额、均价总合计 放入表C
    5. 从表B统计以前年度的各产品的所有面积、金额、均价总合计 操作与上一步类似 放入表C
    6. 从表B统计以后年度的各产品的所有面积、金额、均价总合计 操作与上一步类似 放入表C
    7. 从表B统计各产品取所有的合计 放入表C
    8. 从表C统计累积销售面积、累积销售面积比例,累积销售金额 更新表C
    9. 从表C 列转行,转换后的表只有 产品、统计类型、日期,值4列;(每个产品对应的0-12、13 月对应的值) 放入表D
    10. 从表D 行转列,按类型聚合 求出每个产品每个类型(面积、金额……)的合计 放入表E
    11. 从表E 联接产品表A 与敷项目表查询出最后的显示

以上只是大概思路,过程中会讲一些技巧。

/*临时表说明
#product:用项目过滤后,将“合计”作为一个产品的集合
#TempAllSaleDtl:通过项目过滤后的销售明细,所有月的
#ProductSaleArea:各个产品的总面积,用于计算比例
#TempSaleDtl:通过日期过滤,且加工过后的销售明细,包括增加累积列,以前年度、以后年度、项目合计的记录
#tempSaleDtl2:列转行后的数据集
#tempSaleDtl3:行转列后的数据集 */

设置要查询的参数,以下示例为了好说明,特用2011年作统一说明

DECLARE @ProjectGUID UNIQUEIDENTIFIER
SET @ProjectGUID='8FA659C8-3DA9-4330-B277-9B517E67606D'--要查询的项目
DECLARE @Year CHAR(4)
SET @Year=''--要统一的年份

查找该项目的所有产品放进临时表#product,这里将“合计”作为一个产品的集合也插入产品表#product:

select ProductGUID,ProductName,ProjectGUID,ProductCode into #product from(
select ProductGUID,ProductName,ProjectGUID,ProductName as ProductCode from Product where ProjectGUID=@ProjectGUID
union all
select '00000000-0000-0000-0000-000000000000','合计',@ProjectGUID,'' as ProductCode
) a

查找该项目的所有产品的销售明细放进临时表#TempAllSaleDtl,以作备用:

SELECT ProductGUID,YearMonth,SaleAmount,SaleArea,SaleNum,SalePrice
INTO #TempAllSaleDtl
FROM dbo.SaleDtl WHERE ProductGUID IN (
SELECT ProductGUID FROM dbo.Product WHERE ProjectGUID=@ProjectGUID
)

根据#TempAllSaleDtl现有数据统计,向#TempAllSaleDtl添加总合计记录

--根据现有数据统计,向#TempAllSaleDtl添加总合计记录
insert into #TempAllSaleDtl(ProductGUID,YearMonth,SaleAmount,SaleArea,SaleNum,SalePrice)
select '00000000-0000-0000-0000-000000000000',YearMonth,SUM(SaleAmount),SUM(SaleArea),SUM(SaleNum),SUM(SaleAmount)/SUM(SaleArea)
from #TempAllSaleDtl
group by YearMonth

添加的部分数据如图:

从临时表#TempAllSaleDtl 中查找指定年的销售明细放进临时表#TempSaleDtl ,注意 这个时候就已经包含了 “合计”产品00的数据:

--查找某年的销售明细:#TempSaleDtl
SELECT ProductGUID,YearMonth,SaleAmount,SaleArea,SaleNum,SalePrice,
SalePrice AS ljSaleArea,
SalePrice AS blSaleArea,
SalePrice AS ljSaleAmount
INTO #TempSaleDtl
FROM #TempAllSaleDtl
WHERE LEFT([YearMonth],4)=@Year

从#TempAllSaleDtl 中统计项目各个产品的总销售面积放入表:#ProductSaleArea,主要用作计算 项目累计销售面积比例

--获取项目各个产品的总销售面积:#ProductSaleArea
SELECT ProductGUID,SUM(SaleArea) AS all_SaleArea INTO #ProductSaleArea
FROM #TempAllSaleDtl
GROUP BY ProductGUID

从表#TempSaleDtl 统计当前年度合计列,各产品的所有面积、金额、均价总合计 放入表#TempSaleDtl,注意这里 SUM(SaleAmount)/SUM(SaleArea) 计算销售单价:

--添加2011合计列的记录(本年度的各产品的所有面积、金额、均价总合计)
insert into #TempSaleDtl(
ProductGUID,YearMonth,SaleAmount,SaleArea,SaleNum,SalePrice,ljSaleArea, blSaleArea,ljSaleAmount)
select ProductGUID,@Year+'-13',SUM(SaleAmount),SUM(SaleArea),SUM(SaleNum),SUM(SaleAmount)/SUM(SaleArea),0,0,0
from #TempSaleDtl
group by ProductGUID

部分数据如图,这里有个技巧是用2011-13代表2011整个年份:

从表#TempAllSaleDtl 统计以前年度各产品的所有面积、金额、均价总合计 操作与上一步类似 放入表#TempSaleDtl

--以前年度列记录(本年度以前的各产品的所有面积、金额、均价总合计 操作与上一步类似)
insert into #TempSaleDtl(
ProductGUID,YearMonth,SaleAmount,SaleArea,SaleNum,SalePrice,ljSaleArea, blSaleArea,ljSaleAmount)
select ProductGUID,@Year+'-00',SUM(SaleAmount),SUM(SaleArea),SUM(SaleNum),SUM(SaleAmount)/SUM(SaleArea),0,0,0
from #TempAllSaleDtl where YearMonth <@Year+'-00'
group by ProductGUID

部分数据如图,这里有个技巧是用2011-00代表2011年以前年份:

从表#TempAllSaleDtl 统计以后年度各产品的所有面积、金额、均价总合计 操作与上一步类似 放入表#TempSaleDtl

--以后年度列记录(本年度以后的各产品的所有面积、金额、均价总合计 操作与上一步类似)
insert into #TempSaleDtl(
ProductGUID,YearMonth,SaleAmount,SaleArea,SaleNum,SalePrice,ljSaleArea, blSaleArea,ljSaleAmount)
select ProductGUID,'9999-12',SUM(SaleAmount),SUM(SaleArea),SUM(SaleNum),SUM(SaleAmount)/SUM(SaleArea),0,0,0
from #TempAllSaleDtl where YearMonth >cast((cast(@Year as int) +1) as CHAR(4))+'-00'
group by ProductGUID

部分数据如图,这里有个技巧是用9999-12代表2011年以后年份:

从表#TempAllSaleDtl 统计各产品取所有的合计 放入表#TempSaleDtl

--项目合计列记录(各产品取所有的合计。与上面的区别在于没有添加 here YearMonth >cast((cast(@Year as int) +1) as CHAR(4))+'-00')
insert into #TempSaleDtl(
ProductGUID,YearMonth,SaleAmount,SaleArea,SaleNum,SalePrice,ljSaleArea, blSaleArea,ljSaleAmount)
select ProductGUID,'9999-13',SUM(SaleAmount),SUM(SaleArea),SUM(SaleNum),SUM(SaleAmount)/SUM(SaleArea),0,0,0
from #TempAllSaleDtl
group by ProductGUID

部分数据如图,这里有个技巧是用9999-13代表所有年份:

以上数据中我们的 累积销售面积、累积销售面积比例,累积销售金额三项 之前都用0代替,现在我们来统计。

从表#TempSaleDtl 与 #TempAllSaleDtl统计累积销售面积、累积销售面积比例,累积销售金额 更新表#TempSaleDtl

--更新销售明细TempSaleDtl的累积销售面积、累积销售面积比例,累积销售金额
UPDATE #TempSaleDtl SET
ljSaleArea=b.sum_SaleArea,
ljSaleAmount=b.sum_SaleAmount,
blSaleArea=b.sum_SaleArea/c.all_SaleArea
FROM #TempSaleDtl
left JOIN (
SELECT n.ProductGUID,n.YearMonth,SUM(m.SaleArea) AS sum_SaleArea,SUM(m.SaleAmount) AS sum_SaleAmount
FROM #TempAllSaleDtl m
INNER JOIN #TempSaleDtl n ON m.YearMonth<=n.YearMonth AND m.ProductGUID=n.ProductGUID
GROUP BY n.ProductGUID,n.YearMonth
) b ON #TempSaleDtl.ProductGUID=b.ProductGUID AND #TempSaleDtl.YearMonth=b.YearMonth
LEFT JOIN #ProductSaleArea c ON c.ProductGUID=#TempSaleDtl.ProductGUID

注意这里用到了滑动累计聚合 m.YearMonth<=n.YearMonth(不懂滑动累计聚合请看之前的系列) ,利用子查询统计出 每个产品到当月为止的累计销售面积,累积销售金额,再联接 #ProductSaleArea 更新每个产品的累积销售面积比例。

部分数据如图:

从表#TempSaleDtl 列转行,转换后的表只有 产品、统计类型、日期,值4列;(每个产品对应的0-12、13 月对应的值) 放入表#tempSaleDtl2

--列转行,转换后的表只有 产品、统计类型、日期,值4列;(每个产品对应的0-12、13 月对应的值)
SELECT * INTO #tempSaleDtl2 FROM (
SELECT ProductGUID,'销售套数' AS type,'' AS typecode,YearMonth,MAX(SaleNum) AS val FROM #TempSaleDtl
GROUP BY ProductGUID,YearMonth
UNION ALL
SELECT ProductGUID,'销售面积' AS type,'' AS typecode,YearMonth,MAX(SaleArea) AS val FROM #TempSaleDtl
GROUP BY ProductGUID,YearMonth
UNION ALL
SELECT ProductGUID,'销售均价' AS type,'' AS typecode,YearMonth,MAX(SalePrice) AS val FROM #TempSaleDtl
GROUP BY ProductGUID,YearMonth
UNION ALL
SELECT ProductGUID,'销售金额' AS type,'' AS typecode,YearMonth,MAX(SaleAmount) AS val FROM #TempSaleDtl
GROUP BY ProductGUID,YearMonth
UNION ALL
SELECT ProductGUID,'累计销售面积' AS type, '' AS typecode,YearMonth,SUM(ljSaleArea) FROM #TempSaleDtl
GROUP BY ProductGUID,YearMonth
UNION ALL
SELECT ProductGUID,'累计销售面积比例' AS type, '' AS typecode,YearMonth,SUM(blSaleArea) FROM #TempSaleDtl
GROUP BY ProductGUID,YearMonth
UNION ALL
SELECT ProductGUID,'累计销售金额' AS type, '' AS typecode,YearMonth,SUM(ljSaleAmount) FROM #TempSaleDtl
GROUP BY ProductGUID,YearMonth
) t

部分数据如图:

这里用到的列转行,共有7列,技巧为用code来代表每个类型,也用于显示排序,最终数据为每个产品每个月都有7行数据。这里是不是有了最终结果的雏形?

  

  至此 你真的会玩SQL吗?玩爆你的数据报表之存储过程编写 上篇先写到这,对于看不懂的建议先建立数据库,然后自己一步步试着理下思路,试着写。

这里留个作业,如何将上面的数据转化为下图中的格式呢?

SQL类下载资源已放入公众号【一个码农的日常】 ,回复:数据库 即可,今后会不定期更新

敬请期待下篇,未完待续……

你真的会玩SQL吗?玩爆你的数据报表之存储过程编写(上)的更多相关文章

  1. 你真的会玩SQL吗?你所不知道的 数据聚合

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  2. 你真的会玩SQL吗?之逻辑查询处理阶段

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  3. 你真的会玩SQL吗?和平大使 内连接、外连接

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  4. 你真的会玩SQL吗?三范式、数据完整性

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  5. 你真的会玩SQL吗?让人晕头转向的三值逻辑

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  6. 你真的会玩SQL吗?EXISTS和IN之间的区别

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  7. 你真的会玩SQL吗?无处不在的子查询

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  8. 你真的会玩SQL吗?Case也疯狂

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  9. 你真的会玩SQL吗?表表达式,排名函数

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

随机推荐

  1. 测试一下StringBuffer和StringBuilder及字面常量拼接三种字符串的效率

    之前一篇里写过字符串常用类的三种方式<java中的字符串相关知识整理>,只不过这个只是分析并不知道他们之间会有多大的区别,或者所谓的StringBuffer能提升多少拼接效率呢?为此写个简 ...

  2. 在Linux虚拟机下配置tomcat

    1.到Apache官网下载tomcat http://tomcat.apache.org/download-80.cgi 博主我下载的是tomcat8 博主的jdk是1.8 如果你们的jdk是1.7或 ...

  3. iOS开源项目周报0105

    由OpenDigg 出品的iOS开源项目周报第四期来啦.我们的iOS开源周报集合了OpenDigg一周来新收录的优质的iOS开发方面的开源项目,方便iOS开发人员便捷的找到自己需要的项目工具等. He ...

  4. 关于 devbridge-autocomplete 插件多选操作的实现方法

    目前据我所知最好用的 autocomplete 插件就是 jquery-ui 的 autocomplete 以及 devbridge 的 autocomplete 插件. 我最终选择了 devbrid ...

  5. spring注解源码分析--how does autowired works?

    1. 背景 注解可以减少代码的开发量,spring提供了丰富的注解功能.我们可能会被问到,spring的注解到底是什么触发的呢?今天以spring最常使用的一个注解autowired来跟踪代码,进行d ...

  6. 【HanLP】资料链接汇总

    Java中调用HanLP配置 HanLP自然语言处理包开源官方文档 了解HanLP的全部 自然语言处理HanLP 开源自由的汉语言处理包主页 GitHub源码 基于hanLP的中文分词详解-MapRe ...

  7. git-2.10.2-64-bit介绍&&git下载&&git安装教程

    Git介绍 分布式:Git系统是一个分布式的系统,是用来保存工程源代码历史状态的命令行工具. 保存点:Git的保存点可以追踪源码中的文件, 并能得到某一个时间点上的整个工程项目的状态:可以在该保存点将 ...

  8. MySQL 系列(四)主从复制、备份恢复方案生产环境实战

    第一篇:MySQL 系列(一) 生产标准线上环境安装配置案例及棘手问题解决 第二篇:MySQL 系列(二) 你不知道的数据库操作 第三篇:MySQL 系列(三)你不知道的 视图.触发器.存储过程.函数 ...

  9. C++内联函数

    在C语言中,我们使用宏定义函数这种借助编译器的优化技术来减少程序的执行时间,那么在C++中有没有相同的技术或者更好的实现方法呢?答案是有的,那就是内联函数.内联函数作为编译器优化手段的一种技术,在降低 ...

  10. C语言中如何判断文件是否存在

    方法一:access函数判断文件夹或者文件是否存在 函数原型: int access(const char *filename, int mode); 所属头文件:io.h filename:可以填写 ...