纸飞机

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  22 随笔 :: 28 文章 :: 30 评论 :: 0 Trackbacks

问题:计算某个列中所有值的累计和

解决方案

下面给出了一种解决方案,它展示了如何计算所有职员工资的累计和。为增加可读性,其结果是按SAL排序的,这样就能够很容易地观察到累计和变化的过程。

DB2和Oracle

使用窗口版本的SUM函数计算累计和:

1 select ename, sal,

2         sum(sal) over (order by sal,empno) as running_total

3    from emp

4    order by 2

ENAME              SAL RUNNING_TOTAL

---------- ---------- -------------

SMITH              800            800

JAMES              950           1750

ADAMS             1100           2850

WARD              1250           4100

MARTIN            1250           5350

MILLER            1300           6650

TURNER            1500           8150

ALLEN             1600           9750

CLARK             2450          12200

BLAKE             2850          15050

JONES             2975          18025

SCOTT             3000          21025

FORD              3000          24025

KING              5000          29025

MySQL、PostgreSQL和SQL Server

使用标量子查询计算累计和(由于不使用SUM OVER这类窗口函数,因此就不能像在DB2和Oracle解决方案中那样容易地按SAL给结果排序)。不管怎么说,累计和是正确的(最终结果与上一节相同),但由于没有进行排序,其中间值有所不同:

1 select e.ename, e.sal,

2         (select sum(d.sal) from emp d

3           where d.empno <= e.empno) as running_total

4    from emp e

5   order by 3

ENAME              SAL RUNNING_TOTAL

---------- ---------- -------------

SMITH              800            800

ALLEN             1600           2400

WARD              1250           3650

JONES             2975           6625

MARTIN            1250           7875

BLAKE             2850          10725

CLARK             2450          13175

SCOTT             3000          16175

KING              5000          21175

TURNER            1500          22675

ADAMS             1100          23775

JAMES              950          24725

FORD              3000          27725

MILLER            1300          29025

讨论

生成累计和是因使用新的ANSI窗口函数而得以简化的任务之一。对于不支持这些窗口函数的DBMS,需要使用标量子查询(按取值唯一的字段联接)。

DB2和Oracle

窗口函数SUM OVER能够非常容易地生成累计和。该解决方案中的ORDER BY子句不仅包含SAL列,而且还包含EMPNO列(主键),以避免累计和中出现重复值。下面例子中的RUNNING_TOTAL2列示意了存在重复值时可能带来的问题:

select empno, sal,

        sum(sal)over(order by sal,empno) as running_total1,

        sum(sal)over(order by sal) as running_total2

   from emp

order by 2

ENAME              SAL RUNNING_TOTAL1 RUNNING_TOTAL2

---------- ---------- -------------- --------------

SMITH              800             800             800

JAMES              950            1750            1750

ADAMS             1100            2850            2850

WARD              1250            4100            5350

MARTIN            1250            5350            5350

MILLER            1300            6650            6650

TURNER            1500            8150            8150

ALLEN             1600            9750            9750

CLARK             2450           12200           12200

BLAKE             2850           15050           15050

JONES             2975           18025           18025

SCOTT             3000           21025           24025

FORD              3000           24025           24025

KING              5000           29025           29025

对于WARD、MARTIN、SCOTT和FORD,RUNNING_TOTAL2中的值都不正确。他们的工资分别出现了多次,这些重复值都被加在一起计入累计和。这就是需要使用EMPNO(它是唯一的)才能生成与RUNNING_TOTAL1一样的(正确)结果的原因。大家想一想:对于ADAMS,RUNNING_TOTAL1的值为2850,RUNNING_TOTAL2把WARD的工资1250与2850相加,应该得到4100,然而,RUNNING_TOTAL2却返回了5350,这是为什么呢?因为WARD和MARTIN的SAL相同,他们两个的工资(1250)加在一起就等于2500,然后再加2850,就得到5350。如果指定按不会有重复值的列组合(例如,SAL和EMPNO的取值组合都是唯一的)排序,就能确保生成正确的累计和。

MySQL、PostgreSQL和SQL Server

在这些DBMS完全支持窗口函数之前,可以使用标量子查询计算累计和。一定要按取值唯一的列联接,否则一旦存在像工资重复这样的情况,就会产生不正确的累计和。本节解决方案的关键是把D.EMPNO与E.EMPNO联接起来,它会返回(求和)每个满足D.EMPNO小于或等于E.EMPNO D.SAL。为了更容易理解这些内容,可以重新编写标量子查询,把它写成职员之间的联接:

select e.ename as ename1, e.empno as empno1, e.sal as sal1,

        d.ename as ename2, d.empno as empno2, d.sal as sal2

   from emp e, emp d

where d.empno <= e.empno

    and e.empno = 7566

ENAME           EMPNO1        SAL1 ENAME           EMPNO2        SAL2

---------- ---------- ---------- ---------- ---------- ----------

JONES             7566        2975 SMITH             7369         800

JONES             7566        2975 ALLEN             7499        1600

JONES             7566        2975 WARD              7521        1250

JONES             7566        2975 JONES             7566        2975

EMPNO2中的每个值与EMPNO1中的每个值相比较。对于EMPNO2值小于等于EMPNO1值的所有行,都会把SAL2值加入总和。在这个例子中,职员Smith、Allen、Ward和Jones的EMPNO值都与Jones的EMPNO值相比较。由于这四个职员的EMPNO都满足小于等于Jones的EMPNO的条件,所以会把这些工资加起来;而那些大于Jones的EMPNO的职员都不会计入SUM中。完整的查询的计算方法是:将所有EMPNO小于等于7934(Miller的EMPNO,这个表中的最大值)的所有职员的工资加起来。

posted on 2008-05-14 21:37 纸飞机 阅读(7971) 评论(0)  编辑  收藏 所属分类: Database

只有注册用户登录后才能发表评论。


网站导航: