你真的会玩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. .NET 提升教育 第一期:VIP 付费课程培训通知!

    为响应 @当年在远方 同学的建议,在年前尝试进行一次付费的VIP培训. 培训的课件:点击下载培训周期:10个课程左右,每晚1个半小时培训价格:1000元/人.报名方式:有意向的请加QQ群:路过秋天.N ...

  2. bootstrap + requireJS+ director+ knockout + web API = 一个时髦的单页程序

    也许单页程序(Single Page Application)并不是什么时髦的玩意,像Gmail在很早之前就已经在使用这种模式.通常的说法是它通过避免页面刷新大大提高了网站的响应性,像操作桌面应用程序 ...

  3. HTML5 progress和meter控件

    在HTML5中,新增了progress和meter控件.progress控件为进度条控件,可表示任务的进度,如Windows系统中软件的安装.文件的复制等场景的进度.meter控件为计量条控件,表示某 ...

  4. 利用XAG在RAC环境下实现GoldenGate自动Failover

    概述 在RAC环境下配置OGG,要想实现RAC节点故障时,OGG能自动的failover到正常节点,要保证两点: 1. OGG的checkpoint,trail,BR文件放置在共享的集群文件系统上,R ...

  5. 从源码浅析MVC的MvcRouteHandler、MvcHandler和MvcHttpHandler

    熟悉WebForm开发的朋友一定都知道,Page类必须实现一个接口,就是IHttpHandler.HttpHandler是一个HTTP请求的真正处理中心,在HttpHandler容器中,ASP.NET ...

  6. 【干货分享】流程DEMO-付款申请单

    流程名: 付款申请单  业务描述: 包括每月固定开支.固定资产付款.办公用品付款.工资发放.个人所得税缴纳.营业税缴纳.公积金.社保缴纳和已签订合同的按期付款,最后是出纳付款,出纳核对发票. 流程发起 ...

  7. Android之文件数据存储

    一.文件保存数据介绍 Activity提供了openFileOutput()方法可以用于把数据输出到文件中,具体的实现过程与在J2SE环境中保存数据到文件中是一样的.文件可用来存放大量数据,如文本.图 ...

  8. 在MySQL数据库中创建一个完整的表

    1.登陆成功后,首先进入某一个数据库 (不是指数据库服务器) use t1; //t1是数据库名 如图所示: 2.在此数据库中建立数据库表 2.1 先建立表结构(可以理解为表的列名,也就是字段名)在实 ...

  9. Hibernate 系列 学习笔记 目录 (持续更新...)

    前言: 最近也在学习Hibernate,遇到的问题差不多都解决了,顺便把学习过程遇到的问题和查找的资料文档都整理了一下分享出来,也算是能帮助更多的朋友们了. 最开始使用的是经典的MyEclipse,后 ...

  10. 从史上八大MySQL事故中学到的经验

    本文列举了史上八大MySQL宕机事件原因.影响以及人们从中学到的经验,文中用地震级数来类比宕机事件的严重性和后果,排在最严重层级前两位的是由于亚马逊AWS宕机故障(相当于地震十级和九级). 一.Per ...