問題:計(jì)算某個(gè)列中所有值的累計(jì)和
解決方案
下面給出了一種解決方案,它展示了如何計(jì)算所有職員工資的累計(jì)和。為增加可讀性,其結(jié)果是按SAL排序的,這樣就能夠很容易地觀察到累計(jì)和變化的過程。
DB2和Oracle
使用窗口版本的SUM函數(shù)計(jì)算累計(jì)和:
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
使用標(biāo)量子查詢計(jì)算累計(jì)和(由于不使用SUM OVER這類窗口函數(shù),因此就不能像在DB2和Oracle解決方案中那樣容易地按SAL給結(jié)果排序)。不管怎么說,累計(jì)和是正確的(最終結(jié)果與上一節(jié)相同),但由于沒有進(jìn)行排序,其中間值有所不同:
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
討論
生成累計(jì)和是因使用新的ANSI窗口函數(shù)而得以簡(jiǎn)化的任務(wù)之一。對(duì)于不支持這些窗口函數(shù)的DBMS,需要使用標(biāo)量子查詢(按取值唯一的字段聯(lián)接)。
DB2和Oracle
窗口函數(shù)SUM OVER能夠非常容易地生成累計(jì)和。該解決方案中的ORDER BY子句不僅包含SAL列,而且還包含EMPNO列(主鍵),以避免累計(jì)和中出現(xiàn)重復(fù)值。下面例子中的RUNNING_TOTAL2列示意了存在重復(fù)值時(shí)可能帶來的問題:
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
對(duì)于WARD、MARTIN、SCOTT和FORD,RUNNING_TOTAL2中的值都不正確。他們的工資分別出現(xiàn)了多次,這些重復(fù)值都被加在一起計(jì)入累計(jì)和。這就是需要使用EMPNO(它是唯一的)才能生成與RUNNING_TOTAL1一樣的(正確)結(jié)果的原因。大家想一想:對(duì)于ADAMS,RUNNING_TOTAL1的值為2850,RUNNING_TOTAL2把WARD的工資1250與2850相加,應(yīng)該得到4100,然而,RUNNING_TOTAL2卻返回了5350,這是為什么呢?因?yàn)閃ARD和MARTIN的SAL相同,他們兩個(gè)的工資(1250)加在一起就等于2500,然后再加2850,就得到5350。如果指定按不會(huì)有重復(fù)值的列組合(例如,SAL和EMPNO的取值組合都是唯一的)排序,就能確保生成正確的累計(jì)和。
MySQL、PostgreSQL和SQL Server
在這些DBMS完全支持窗口函數(shù)之前,可以使用標(biāo)量子查詢計(jì)算累計(jì)和。一定要按取值唯一的列聯(lián)接,否則一旦存在像工資重復(fù)這樣的情況,就會(huì)產(chǎn)生不正確的累計(jì)和。本節(jié)解決方案的關(guān)鍵是把D.EMPNO與E.EMPNO聯(lián)接起來,它會(huì)返回(求和)每個(gè)滿足D.EMPNO小于或等于E.EMPNO D.SAL。為了更容易理解這些內(nèi)容,可以重新編寫標(biāo)量子查詢,把它寫成職員之間的聯(lián)接:
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中的每個(gè)值與EMPNO1中的每個(gè)值相比較。對(duì)于EMPNO2值小于等于EMPNO1值的所有行,都會(huì)把SAL2值加入總和。在這個(gè)例子中,職員Smith、Allen、Ward和Jones的EMPNO值都與Jones的EMPNO值相比較。由于這四個(gè)職員的EMPNO都滿足小于等于Jones的EMPNO的條件,所以會(huì)把這些工資加起來;而那些大于Jones的EMPNO的職員都不會(huì)計(jì)入SUM中。完整的查詢的計(jì)算方法是:將所有EMPNO小于等于7934(Miller的EMPNO,這個(gè)表中的最大值)的所有職員的工資加起來。