在Sqlserver下巧用行列转换日期的数据统计

前言

在SQLSERVER 中有很多统计函数的基础语法,有使用Group By 或 partition by 后配合Sum,Count(*) 等用法。常应用于统计网站的PV流量、合同项目中月收入等业务场景中。在文中我分享下最近做过的统计小案例,和大家互相学习下:)

背景

合同中行项目按月收入的统计

1.业务逻辑及需求 

1.1 表业务逻辑

合同是公司间互相签署的法律契约,一份合同从诞生起,就开始流转于公司的各个部门,最核心的还是盈亏的数值。盈亏是结果,数据的产生源于每个自然月或其他时段的汇总。 往往在实际业务中,例如有些广告行业,立项是分为固定排期和合同活动收入。

固定排期一般以一个自然月为周期,例如[201503,201504]间产生的预收入;活动收入表中的活动是指收入周期不固定,可能ConfirmDate  发生在一个月中的若干天中,也可能在间隔一个月后发生。

无论是固定排期还是活动收入都和行项目有关,行项目是一个编号,一个行项目可以对应多次排期或活动收入的统计,在我给大家介绍的Demo中,将暂时考虑固定排期的情况。

1.2 项目的需求

统计合同中行项目的金额:分为结转金额数据汇总,和按自然月条件下金额的汇总。

2.准备的基础表

2.1 合同信息表

CREATE TABLE  ContractInfo --基本信息表
(
[ContractCode] [varchar](50) Primary key
,[CustomName] [varchar](100) NULL,
) insert into ContractInfo
(ContractCode,CustomName)
values('','弘化四方')
,('','明心见性')
,('','心绽莲花')

2.2 合同行项目表

CREATE TABLE ContractLine --合同行项目表
(
[LineID] [int] IDENTITY(1,1) Primary Key NOT NULL,
[ContractCode] [varchar](50) NOT NULL,
) insert into ContractLine
(ContractCode)
values('')
,('')
,('')
,('')
,('')

2.3 合同固定排期表

CREATE TABLE ContractSchedule  --合同固定排期表(
[ScheduleID] [int] Primary key NOT NULL,-- 排期ID
[LineID] [int] NOT NULL, -- 行项目ID
[Period] [int] NOT NULL, --时间段
[Amount] [decimal](18, 2) NOT NULL, --交易金额
) insert into ContractSchedule
(ScheduleID,LineID,Period,Amount)
values
(89106,1,201507,90900.00)
,(89107,1,201508,9453.00)
,(89108,1,201510,13000.00)
,(89109,2,201501,12000.00)
,(89110,2,201503,11000.00)
,(89111,3,201509,9000.00)
,(89112,4,201510,8500.00)

3.补充其他(待)

基础知识点

1.FOR XML PATH  //用于统计时转换行列的格式,

参考:王波洋老师的 灵活运用 FOR XML PATH

2.PIVOT (SUM(Amount)) For Period //用于基础表基础上的行列转换,

参考:大志若愚老师纵表、横表互转的SQL

3.Select SUM(Amount) From ContractSchedule

group by LineID // 根据条件汇总数据

实现思路

逻辑 

/*计算时间的基础序列*/ ->/*格式化日期序列*/ -> /*关联逻辑表,查询计算8月份之前的汇总,8月份之后的按月份统计*/

代码片段

 /*---------------计算时间的基础序列------------*/

 /*获取日期序列起始值*/
DECLARE @sdate CHAR(10);
DECLARE @edate CHAR(10);  
SET @sdate = '2015-08-01'--开始日期
SET @edate = '2015-12-1' /*存入临时表*/
SELECT * into #DateArr
from (
select
CONVERT(varchar(6),DATEADD(MONTH,a.number,@sdate),112) totalDate
FROM master..spt_values a --系统表
WHERE a.type = 'P'
AND number BETWEEN 0 AND (select DATEDIFF(MONTH,@sdate,@edate))
)a select * from #DateArr
 /*格式化日期序列,用@Months接收*/
DECLARE @Months VARCHAR(1000);
DECLARE @SQL NVARCHAR(MAX); SET @SQL = 'SELECT @Months=STUFF((SELECT DISTINCT '',[''+totalDate+'']'' FROM #DateArr s
FOR XML PATH('''')),1,1,'''')';
EXECUTE sp_executesql @SQL,N'@Months VARCHAR(1000) OUTPUT',@Months OUTPUT; print @Months
 /*未关联时间序列前的基础数据*/
with tab as(
select
c.ContractCode
,c.CustomName
,cl.LineID
,ISNULL(b.TheEndYearAmount,0) as NearAYearAgo
,cs.Amount
,cs.Period
from ContractInfo c
left join
ContractLine cl
on c.ContractCode=cl.ContractCode
left join
ContractSchedule cs
on cs.LineID=cl.LineID
--计算8月份之前的统计
left join
(
select LineID,Sum(Amount) as TheEndYearAmount
from
ContractSchedule
where Period between 201508 and 201512
group by LineID
--select * from ContractSchedule
)b on b.LineID=cl.LineID
) select * from tab
 /*--------添加日期序列后的统计 --------*/
SET @SQL='
with tab as(
select c.CustomName
,ISNULL(b.TheEndYearAmount,0) as NearAYearAgo
,c.ContractCode --合同号
,cl.LineID --合同的行ID
,cs.Amount --待计算的数量
,cs.Period --统计的日期
from ContractInfo c
left join
ContractLine cl
on c.ContractCode=cl.ContractCode
left join
ContractSchedule cs
on cs.LineID=cl.LineID
--计算8月份之前的统计
left join
(
select LineID,Sum(Amount) as TheEndYearAmount
from
ContractSchedule
where Period between 201412 and 201508
group by LineID
--select * from ContractSchedule
)b on b.LineID=cl.LineID
) select * from tab
PIVOT (SUM(Amount) FOR Period
IN(
'+@Months+'
))b
'
EXEC (@SQL)

查询后结果   脚本下载

思考

留下的思考

1. 对空值的处理: select * from tab PIVOT (SUM(Amount)...

这里我尝试用ISNULL(SUM(Amount),0.00) 去处理,但语法没有通过,我将继续尝试..

2. 脚本片段中获取日期序列,或许在其他统计脚本中也会复用,我准备写到标量函数或表值函数中试一下。

3. 常用的业务统计脚本中关联的表比较多,如何能有效避免重复,在最后结果集中减少使用 distinct ,而使用Group by 去过滤重复字段

这一个知识点我比较薄弱,不断总结,在分享经验给大家,少走弯路。

感谢

我的好朋友欢,一直致力于SQL方面的统计,他给了我很多建议{

1.理解需求并开始写之前,要知道每个表里会出现什么数据

2.出现问题后,先查表与表之间是什么关联,关联从少到多,去检查错误

3.最核心的想清楚再写sql,如果脑子里不清楚就上手写,万一出现一个错误的想法,再纠正就麻烦了 

}

博学的龙叔,总是第一时间帮助大家理清混乱的逻辑。

永远的涛哥,在不断修改涛哥的统计脚本中,使自己受益匪浅。

在Sqlserver下巧用行列转换日期的数据统计的更多相关文章

  1. 简单的叙述下SQL中行列转换的小知识!

    行列转换对于工作还是学习中总是不可避免的会遇到(虽然本人还尚未工作,萌萌哒的学生一枚),解决的方法也有很多,我这里就总结一下我所想解决的问题以及怎么去解决的方法, 可能网上已经有很多类似的方法了,有的 ...

  2. MySQL中实现连续日期内数据统计,缺省天数0补全

    某一日,需要查询订单表中一个月每天的金额数 查询出数据如下: array(14) { [0] => array(2) { ["money"] => string(7) ...

  3. 数据库行列转换sql

    经常折腾数据库,常常遇到数据库行列转换的问题,下面就用一个小例子来演示下如何进行行列转换. 1.创建一张表 CREATE TABLE [android_source]( [CREATETIME] [d ...

  4. 【转】在sqlserver下增加MYSQL的链接服务器,实现分布式数据库开发第一步

    首先要在SQLserver上服务器上这装ODBC对mysql的支持,我下载了mysql-connector-odbc-5.1.5-win32.rar,安装后在ODBC中有了DRIVER={MySQL ...

  5. SQLServer树形数据结构的数据进行数据统计

    前言 前几天朋友问我,关于SQLServer数据库中对树形结构的表数据统计问题,需求大致如下: 分类表(递归数据),A的子分类是B,B的子分类是C--分类关系不间断,A为第一层,B为第二层,C为第三层 ...

  6. sqlserver表分区与调优与行列转换

    转自: http://www.cnblogs.com/knowledgesea/p/3696912.html http://www.open-open.com/lib/view/open1418462 ...

  7. SqlServer2000下实现行列转换

    SqlServer2000下实现行列转换 2011-04-06 22:07:07|  分类: SQL Server |  标签:sqlserver  2000  行列转换  sql  |举报|字号 订 ...

  8. SQLserver中用convert函数转换日期格式

    SQLserver中用convert函数转换日期格式 2008-01-23 15:47 SQLserver中用convert函数转换日期格式2008-01-15 15:51SQLserver中用con ...

  9. SQLserver中用convert函数转换日期格式(1)

    SQLserver中用convert函数转换日期格式2008-01-15 15:51SQLserver中用convert函数转换日期格式 SQL Server中文版的默认的日期字段datetime格式 ...

随机推荐

  1. Tomcat启动时自动加载Servlet

    1.想做一个服务启动时自动启动一不停止的获取订阅功能 2.之前是做一个Jsp页面请求servlet来触发方法 3.现在实现Tomcat启动时自动加载Servlet 1.Tomcat中启动Servlet ...

  2. iOS返回一个前面没有0,小数点后保留两位的数字字符串

    /* * 处理一个数字加小数点的字符串,前面无0,保留两位.网上有循环截取的方法,如果数字过长,浪费内存,这个方法在优化内存的基础上设计的. */ -(NSString*)getTheCorrectN ...

  3. JVM基本结构

    以下是JVM的一个基本架构图,在这个基本架构图中,栈有两部份,Java线程栈以及本地方法栈,栈的概念与C/C++程序基本上都是一个概念,里面存放的都是栈帧,一个栈帧代表的就是一个函数的调用,在栈帧里面 ...

  4. linux基础命令之:vi模式下查找和替换

    一.查找 查找命令 /pattern<Enter> :向下查找pattern匹配字符串 ?pattern<Enter>:向上查找pattern匹配字符串 使用了查找命令之后,使 ...

  5. AJAX实现跨域的三种种方法(代理,JSONP,XHR2)

    由于在工作中需要使用AJAX请求其他域名下的请求,但是会出现拒绝访问的情况,这是因为基于安全的考虑,AJAX只能访问本地的资源,而不能跨域访问. 比如说你的网站域名是aaa.com,想要通过AJAX请 ...

  6. c#-基础:类的进阶

    类的概述: 类是一个能存储数据并执行代码的数据结构 数据成员:通常模拟该类所表示显示世界的事物特性 函数成员:执行代码.模拟显示世界事物的功能和操作 数据成员:字段,常量 函数成员执行代码:方法 运算 ...

  7. setTimeout 和 setInterval

    设置定时器,在一段时间之后执行指定的代码,setTimeout与setInterval的区别在于setTimeout函数指定的代码仅执行一次 方法一: window.setTimeout(" ...

  8. XidianOJ 1063 Chemistry Problem

    [提交][状态][讨论版] 题目描述 lw最近正在学习立体化学.立体化学中常用Fischer投影式表示分子的立体构型,例如,对于酒石酸HOOC(CHOH)2COOH,如果用一根横线表示羟基,略去氢原子 ...

  9. [JSP]用户注册

    //----------------------userRegister.jsp <%@ page contentType="text/html;charset=gb2312" ...

  10. 配置移动前端开发调试环境(nodejs+npm+weiner的安装和配置使用)

    这段时间发现做移动端的开发调试是一大难题,网上逛了逛发现有一些工具可用,如chrome的远程调试,实际测试过程中我始终调试不成功,听说被墙后是不行的,所以最终找了如下的方法. 因为基于nodeJS环境 ...