设为首页 加入收藏

TOP

锋利的SQL2014:基于窗口的聚合计算(三)
2015-11-21 01:30:42 来源: 作者: 【 】 浏览:4
Tags:锋利 SQL2014 基于 窗口 聚合 计算
.Orders;

该语句的执行结果如表9-7所示。前4行RunSubTotal列都计算为1000,这是为什么呢?我们在前面强调过,排序对于范围的限定非常重要,注意上面的ORDER BY子句限定了按EmpID和SalesYear列排序。由表9-7中可以看到,前4行的EmpID和SalesYear值是相同的,因此RANGE将范围限定为这4行,所以SUM对这4行的SubTotal列进行求和,结果为1000。这里需要注意的是,我们说的用于限定范围的相同值,是指的排序列的值相同,而不是被聚合列的值相同。

\

上面示例中的排序列存在重复值,所以在使用RANGE时存在对多行聚合现象,当排序列的值具有唯一性时,RANGE和ROWS的限定范围实际上是一样的。例如,下面的两条语句一条使用了ROWS,一条使用了RANGE,其他完全相同。因为排序列EmpID+SalesYear+SalesQuarter的值具有唯一性,所以这两条语句的返回结果是相同的,如表9-8所示。

SELECT EmpID, SalesYear, SalesQuarter, SubTotal,

SUM(SubTotal) OVER(PARTITION BY EmpID

ORDER BY EmpID, SalesYear, SalesQuarter

RANGE BETWEEN UNBOUNDED PRECEDING

ANDCURRENT ROW) AS RunSubTotal

FROM dbo.Orders;



SELECT EmpID, SalesYear, SalesQuarter, SubTotal,

SUM(SubTotal) OVER(PARTITION BY EmpID

ORDER BY EmpID, SalesYear, SalesQuarter

ROWS BETWEEN UNBOUNDED PRECEDING

ANDCURRENT ROW) AS RunSubTotal

FROM dbo.Orders;

\

9.2.3 开窗聚合函数的嵌套

前面我们讲过,窗口的分区与数据分组有类似之处,但是,在一条语句中指定分组列后,对于整个语句你只能按此列进行分组,而分区则可以在一条语句中指定多个不同的分区方式,这两种方式似乎不存在相交之处。实际上,两者也可以结合在一起使用。

仍旧以9.2.1节创建的Orders表为例,假设现在要计算雇员的年度销售额和企业整体的年度销售额,可以使用下面的语句。雇员的年度销售额按SalesYear+EmpID列分区,企业的年度销售额直接按SalesYear分区。查询结果如表9-9所示。

WITH CTE

AS(

SELECTEmpID, SalesYear,

SUM(SubTotal) OVER(PARTITION BY SalesYear, EmpID) AS EmpSubTotal,

SUM(SubTotal) OVER(PARTITION BY SalesYear) AS YearSubTotal

FROMdbo.Orders

)

SELECT DISTINCT * FROM CTE;

表9-9 计算雇员年度销售额和企业年度销售额

EmpID

SalesYear

EmpSubTotal

YearSubTotal

1

2013

1000

2200

2

2013

1200

2200

1

2014

600

1400

2

2014

800

1400

由表9-9可以看出,企业2013年销售额是2200,2014年是1400。2013年雇员1和雇员2的销售额分别是1000和1200。2014年雇员1和雇员2的销售额分别是600和800。

再来看下面一条语句,它的执行结果与表9-5完全相同。

SELECT EmpID, SalesYear,

SUM(SubTotal) AS EmpSubTotal,

SUM(SUM(SubTotal)) OVER(PARTITION BYSalesYear) AS YearSubTotal

FROM dbo.Orders

GROUP BY EmpID, SalesYear

上面这条语句中使用GROUPBY按EmpID+SalesYear进行了分组计算,得到的是每位雇员的年度销售额SUM(SubTotal),而语句中的嵌套开窗函数SUM(SUM(SubTotal))(注意,最外层的SUM是开窗聚合函数,里面的SUM(SubTotal)是每位雇员的年度销售额)是对每位雇员的年度销售额进行求和,从而得到企业的年度销售额。当然,这里有一个问题,语句中第2行的SUM(SubTotal)是GROUP BY的计算结果,相对于第3行中的SUM(SUM(SubTotal))而言,它是一个中间结果,为什么这条语句能够正确执行呢?让我们再来回顾一下在5.10节中介绍的查询的逻辑处理顺序——SELECT子句是在GROUP BY后面执行的,因此SUM(SubTotal)这个中间结果对于开窗函数而言是可见的。不过,在这条语句中只能使用函数嵌套,而不能使用别名EmpSubTotal。我们在5.9节介绍过“同时操作”概念,这时候的别名EmpSubTotal对于后面的SUM函数并不可见。例如,下面语句将引发错误,提示“列名'EmpSubTotal'无效”。

SELECT EmpID, SalesYear,

SUM(SubTotal) AS EmpSubTotal,

SUM(EmpSubTotal) OVER(PARTITION BYSalesYear) AS YearSubTotal

FROM dbo.Orders

GROUP BY EmpID, SalesYear

从上面的分析可以看出,开窗聚合函数支持函数嵌套,但是对于语句可读性而言,增加了阅读难度。可以通过公用表表达式的方法进行简化,参考下面的语句。

WITH CTE AS (

SELECTEmpID, SalesYear, SUM(SubTotal) AS EmpSubTotal

FROMdbo.Orders

GROUP BYEmpID, SalesYear

)SELECT EmpID, SalesYear, EmpSubTotal,

SUM(EmpSubTotal) OVER(PARTITION BY SalesYear) AS YearSubTotal

FROM CTE;

9.2.4 分区聚合计算与联接的比较

前面我们讲过,分区可以使用子查询来代替,此外,也可以通过联结的方法实现类似功能,并且联结有时比子查询和分区更为高效。下面的示例使用在9.1节创建的Students表,现假设需要计算每名学生成绩与本班级平均成绩的差异。我们需要先计算每个班级的平均成绩,然后通过联接的方式将平均成绩关联到相应的学生成绩行,再计算差异。语句如下:

SELECT S1.ClassID,

S1.StudentName,

S1.Achievement,

S2.AvgAch ,

S1.Achievement - S2.AvgAch AS Diff

FROM Students AS S1

LEFT OUTERJOIN (SELE
首页 上一页 1 2 3 4 下一页 尾页 3/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇锋利的SQL2014:基于窗口的分布计.. 下一篇报表开发实例――动态多层次KPI钻..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: