按月份进行统计

在应用当中时常需要按照月份进行统计,比如整年的产量,一月的常量,二月的产量。。。十二月的常量。在表的设计的时候产量表一般有两个列,一个是时间,一个是产量。每月的产量对应表中的多条记录。一般产生的报表有如下格式:

id Total Jan Feb Mar Apr May June July Aug Sep Oct Nov Dec
id 12x x x x x x x x x x x x x

下面对Nothwind数据库中的雇员2007年的销售情况按月进行统计。首先对这些数据进行感性认识下:

SELECT empid, orderdate, unitprice, qty
FROM Sales.Orders o JOIN Sales.OrderDetails od
ON o.orderid = od.orderid
WHERE DATEPART(YEAR, o.orderdate) = 2007
ORDER BY empid, orderdate

可以得到如下数据,我只拿empid = 1的数据:

1 2007-01-01 00:00:00.000 99.00 21
1 2007-01-01 00:00:00.000 14.40 35
1 2007-01-01 00:00:00.000 16.00 30
  ……………    
1 2007-02-21 00:00:00.000 10.20 15
1 2007-02-21 00:00:00.000 12.00 25
  ……………    
1 2007-03-05 00:00:00.000 3.6 25
  ……………    
  ……………    
  ……………    
1 2007-12-11 00:00:00.000 39.00 33
  ……………    

empid 为1 的每月在表中都有多条记录。下面采用两种方式去产生需要的报表。

1)使用SUM聚合函数,因为sum参数可以是表达式,那么我使用case逻辑对sum聚合进行控制,比如对一月统计可以采用如下方式:

	SUM(CASE DATEPART(MONTH, o.orderdate)
			WHEN 1 THEN 1
			ELSE 0
		END * od.unitprice*od.qty) AS Jan
采用如上方式,我只要在select list里面重复上面的聚合函数,就可以得到想要的报表。
SELECT empid AS Empid,
	SUM(od.unitprice * od.qty) AS Year,
	SUM(CASE DATEPART(MONTH, o.orderdate)
			WHEN 1 THEN 1
			ELSE 0
		END * od.unitprice*od.qty) AS Jan,
	SUM(CASE DATEPART(MONTH, o.orderdate)
			WHEN 2 THEN 1
			ELSE 0
		END * od.unitprice*od.qty) AS Feb,
	SUM(CASE DATEPART(MONTH, o.orderdate)
			WHEN 3 THEN 1
			ELSE 0
		END * od.unitprice*od.qty) AS Mar,
	SUM(CASE DATEPART(MONTH, o.orderdate)
			WHEN 4 THEN 1
			ELSE 0
		END * od.unitprice*od.qty) AS Apr,
	SUM(CASE DATEPART(MONTH, o.orderdate)
			WHEN 5 THEN 1
			ELSE 0
		END * od.unitprice*od.qty) AS May,
	SUM(CASE DATEPART(MONTH, o.orderdate)
			WHEN 6 THEN 1
			ELSE 0
		END * od.unitprice*od.qty) AS June,
	SUM(CASE DATEPART(MONTH, o.orderdate)
			WHEN 7 THEN 1
			ELSE 0
		END * od.unitprice*od.qty) AS July,
	SUM(CASE DATEPART(MONTH, o.orderdate)
			WHEN 8 THEN 1
			ELSE 0
		END * od.unitprice*od.qty) AS Aug,
	SUM(CASE DATEPART(MONTH, o.orderdate)
			WHEN 9 THEN 1
			ELSE 0
		END * od.unitprice*od.qty) AS Seo,
	SUM(CASE DATEPART(MONTH, o.orderdate)
			WHEN 10 THEN 1
			ELSE 0
		END * od.unitprice*od.qty) AS Oct,
	SUM(CASE DATEPART(MONTH, o.orderdate)
			WHEN 11 THEN 1
			ELSE 0
		END * od.unitprice*od.qty) AS Nov,
	SUM(CASE DATEPART(MONTH, o.orderdate)
			WHEN 12 THEN 1
			ELSE 0
		END * od.unitprice*od.qty) AS Dec	
FROM Sales.Orders o JOIN Sales.OrderDetails od
ON o.orderid = od.orderid
WHERE DATEPART(YEAR, orderdate) = 2007
GROUP BY empid
ORDER by empid

以上的方式逻辑比较清晰,但是list比较长,下面这种方式原理和上面一样,但是做法更巧妙。

SELECT empid Empid, 
	SUM(od.qty * od.unitprice) Year,
	SUM(od.qty *od.unitprice * (1-ABS(SIGN(DATEPART(MONTH, o.orderdate) - 1)))) AS Jan,
	SUM(od.qty *od.unitprice * (1-ABS(SIGN(DATEPART(MONTH, o.orderdate) - 2)))) AS Feb,
	SUM(od.qty *od.unitprice * (1-ABS(SIGN(DATEPART(MONTH, o.orderdate) - 3)))) AS Mar,
	SUM(od.qty *od.unitprice * (1-ABS(SIGN(DATEPART(MONTH, o.orderdate) - 4)))) AS Apr,
	SUM(od.qty *od.unitprice * (1-ABS(SIGN(DATEPART(MONTH, o.orderdate) - 5)))) AS May,
	SUM(od.qty *od.unitprice * (1-ABS(SIGN(DATEPART(MONTH, o.orderdate) - 6)))) AS June,
	SUM(od.qty *od.unitprice * (1-ABS(SIGN(DATEPART(MONTH, o.orderdate) - 7)))) AS July,
	SUM(od.qty *od.unitprice * (1-ABS(SIGN(DATEPART(MONTH, o.orderdate) - 8)))) AS Aug,
	SUM(od.qty *od.unitprice * (1-ABS(SIGN(DATEPART(MONTH, o.orderdate) - 9)))) AS Sep,
	SUM(od.qty *od.unitprice * (1-ABS(SIGN(DATEPART(MONTH, o.orderdate) - 10)))) AS Oct,
	SUM(od.qty *od.unitprice * (1-ABS(SIGN(DATEPART(MONTH, o.orderdate) - 11)))) AS Nov,
	SUM(od.qty *od.unitprice * (1-ABS(SIGN(DATEPART(MONTH, o.orderdate) - 12)))) AS Dec	
FROM Sales.Orders o JOIN Sales.OrderDetails od
ON o.orderid = od.orderid
WHERE DATEPART(YEAR, orderdate) = 2007
GROUP BY empid
ORDER by empid

注意上面sum函数的表达是,比如对于一月,有:

od.qty *od.unitprice * (1-ABS(SIGN(DATEPART(MONTH, o.orderdate) - 1)))

如果当前的记录是一月,那么DATEPART(MONTH, o.orderdate)是1, SIGN(DATEPART(MONTH, o.orderdate) – 1) = SIGN(1-1) =SIGN(0) = 0,

od.qty*od.unitprice*(1-ABS(SIGN(DATEPART(MONTH, o.orderdate)-1))) = od.qty * od.unitprice*(1-ABS(0)) = od.qty * od.unitprice * 1

如果当前的记录不是一月,那么DATEPART(MONTH, o.orderdate) >1, SIGN(DATEPART(MONTH, o.orderdate) – 1) = SIGN(>1) = 1,

od.qty*od.unitprice*(1-ABS(SIGN(DATEPART(MONTH, o.orderdate)-1))) = od.qty * od.unitprice*(1-ABS(1)) = od.qty * od.unitprice * 0 = 0

所以od.qty *od.unitprice * (1-ABS(SIGN(DATEPART(MONTH, o.orderdate) - 1)))计算一月的,

SUM(od.qty *od.unitprice * (1-ABS(SIGN(DATEPART(MONTH, o.orderdate) - 2))))计算二月的。。。

2)使用Pivot进行旋转。

首先得到每个id每个月的销售量,如下:

	SELECT empid, DATEPART(MONTH, o.orderdate) Month, unitprice * qty Total
	FROM Sales.Orders o JOIN Sales.OrderDetails od
	ON o.orderid = od.orderid
	WHERE DATEPART(YEAR, o.orderdate) = 2007

接下来对按Month列进行旋转,对Total列进行sum聚合计算:

SELECT empid EmpId,
	ISNULL(p.[1], 0) + ISNULL(p.[2],0) + ISNULL(p.[3], 0)+ISNULL(p.[4],0)+
	ISNULL(p.[5], 0)+ISNULL(p.[6],0) + ISNULL(p.[7],0)+ISNULL(p.[8],0)+ISNULL(p.[9],0)+
	ISNULL(p.[10],0)+ISNULL(p.[11],0)+ISNULL(p.[12],0) As Total,
	P.[1] As Jan, P.[2] Feb, P.[3] AS Mar, P.[4] AS Apr,P.[5] May, P.[6] June, P.[7] July,
	P.[8] Aug, P.[9] Sep, P.[10] Oct, P.[11] Nov, P.[12] Dec
FROM
(
	SELECT empid, DATEPART(MONTH, o.orderdate) Month, unitprice * qty Total
	FROM Sales.Orders o JOIN Sales.OrderDetails od
	ON o.orderid = od.orderid
	WHERE DATEPART(YEAR, o.orderdate) = 2007
) D1 PIVOT(
	SUM(Total)
	FOR Month IN ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])
	) AS P
ORDER BY empid

注意如果表中存在某个employee某月没有销售记录,那么对应的结果null(不可以的),那么就需要ISNULL把null转成0,否则Total为null。

那也可以先按empid,月份进行分组,然后在进行旋转,如下:

SELECT  empid EmpId,
	ISNULL(p.[1], 0) + ISNULL(p.[2],0) + ISNULL(p.[3], 0)+ISNULL(p.[4],0)+
	ISNULL(p.[5], 0)+ISNULL(p.[6],0) + ISNULL(p.[7],0)+ISNULL(p.[8],0)+ISNULL(p.[9],0)+
	ISNULL(p.[10],0)+ISNULL(p.[11],0)+ISNULL(p.[12],0) As Total,
	P.[1] As Jan, P.[2] Feb, P.[3] AS Mar, P.[4] AS Apr,P.[5] May, P.[6] June, P.[7] July,
	P.[8] Aug, P.[9] Sep, P.[10] Oct, P.[11] Nov, P.[12] Dec
FROM (
	SELECT empid,  DATEPART(MONTH, o.orderdate) month, SUM(od.unitprice * od.qty) total
	FROM Sales.Orders o JOIN Sales.OrderDetails od
	ON o.orderid = od.orderid
	WHERE DATEPART(YEAR, o.orderdate) = 2007
	GROUP BY empid,DATEPART(MONTH, o.orderdate)
) D1 PIVOT(
	MAX(total)
	FOR month IN ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])
	) AS P
ORDER BY empid

以上一共采用四种不同的方式,两种不同的原理对数据进行统计。

原文地址:https://www.cnblogs.com/fgynew/p/1669989.html