問題:求特定列中的值占總和的百分比。例如,確定所有DEPTNO 10工資占總工資的百分比(DEPTNO 10的工資在總工資中的百分比數)。
解決方案
總的來說,在SQL中計算占總數的百分比跟書面計算一樣:先除后乘。這個例子要計算表EMP中DEPTNO 10工資所占的百分比。首先,算出DEPTNO 10的工資,然后除以表中的工資總和,最后一步,乘以100,則返回一個表示百分比的值。
MySQL和PostgreSQL
DEPTNO 10的工資總和除以所有工資總和:
1 select (sum(
2 case when deptno = 10 then sal end)/sum(sal)
3 )*100 as pct
4 from emp
DB2、Oracle和SQL Server
使用內聯視圖及窗口函數SUM OVER,計算出所有工資總和以及DEPTNO 10的工資和。然后,在外層查詢中進行除法和乘法操作:
1 select distinct (d10/total)*100 as pct
2 from (
3 select deptno,
4 sum(sal)over() total,
5 sum(sal)over(partition by deptno) d10
6 from emp
7 ) x
8 where deptno=10
討論
MySQL和PostgreSQL
用CASE語句能夠輕松地得到DEPTNO 10的工資。然后將它們加起來,并除以所有工資總和。由于聚集時會忽略NULL值,所以CASE語句中不必加入ELSE子句。如果想看到確切的被除數和除數,則可以執行不做除法的查詢:
select sum(case when deptno = 10 then sal end) as d10,
sum(sal)
from emp
D10 SUM(SAL)
---- ---------
8750 29025
依定義SAL的方式不同,在進行除法操作時可能需要做顯式類型轉換。例如,在DB2、SQL Server和PostgreSQL中,如果SAL定義為整數,則可以把它轉換為小數,以便得到正確答案,如下所示:
select (cast(
sum(case when deptno = 10 then sal end)
as decimal)/sum(sal)
)*100 as pct
from emp
DB2、Oracle和SQL Server
除傳統解決方案外,該方案使用窗口函數計算相對于總數的百分數。對于DB2和SQL Server,如果SAL定義為整數類型,則在除法操作之前,需要進行類型轉換:
select distinct
cast(d10 as decimal)/total*100 as pct
from (
select deptno,
sum(sal)over() total,
sum(sal)over(partition by deptno) d10
from emp
) x
where deptno=10
必須記住,窗口函數在WHERE子句后執行。因此不能把針對DEPTNO的篩選放在內聯視圖X中。分別考慮一下內聯視圖X中包含及不包含DEPTNO篩選的結果。首先,看一下不包含DEPTNO篩選的結果:
select deptno,
sum(sal)over() total,
sum(sal)over(partition by deptno) d10
from emp
DEP
------- --------- ---------
10 29025 8750
10 29025 8750
10 29025 8750
20 29025 10875
20 29025 10875
20 29025 10875
20 29025 10875
20 29025 10875
30 29025 9400
30 29025 9400
30 29025 9400
30 29025 9400
30 29025 9400
30 29025 9400
包含DEPTNO篩選的結果:
select deptno,
sum(sal)over() total,
sum(sal)over(partition by deptno) d10
from emp
where deptno=10
DEPTNO TOTAL D10
------ --------- ---------
10 8750 8750
10 8750 8750
10 8750 8750
由于窗口函數在WHERE子句后執行,因此TOTAL的值僅表示DEPTNO 10的工資之和,而實際上需要用TOTAL表示所有工資的總和。這就是必須把針對DEPTNO的篩選放在內聯視圖X外面的原因。