【實驗】分析函式之魅力展示

secooler發表於2009-10-05
眾所周知,Oracle的分析函式在BI類的分析資料庫中起到了非常大的作用,不僅僅是因為它能做的事情非常的豐富,同時,分析函式作為Oracle的內部實現,較之其他的實現形式(例如使用group by實現)效率也是出奇的高。

透過這個例子展示一下分析函式的一點點魅力。


1.樣本資料如下
sec@ora10g> select * from emp;

EMPNO ENAME   JOB        MGR HIREDATE              SAL COMM DEPTNO
----- ------- --------- ---- ------------------- ----- ---- ------
 7369 SMITH   CLERK     7902 1980-12-17 00:00:00   800          20
 7499 ALLEN   SALESMAN  7698 1981-02-20 00:00:00  1600  300     30
 7521 WARD    SALESMAN  7698 1981-02-22 00:00:00  1250  500     30
 7566 JONES   MANAGER   7839 1981-04-02 00:00:00  2975          20
 7654 MARTIN  SALESMAN  7698 1981-09-28 00:00:00  1250 1400     30
 7698 BLAKE   MANAGER   7839 1981-05-01 00:00:00  2850          30
 7782 CLARK   MANAGER   7839 1981-06-09 00:00:00  2450          10
 7788 SCOTT   ANALYST   7566 1982-12-09 00:00:00  3000          20
 7839 KING    PRESIDENT      1981-11-17 00:00:00  5000          10
 7844 TURNER  SALESMAN  7698 1981-09-08 00:00:00  1500    0     30
 7876 ADAMS   CLERK     7788 1983-01-12 00:00:00  1100          20
 7900 JAMES   CLERK     7698 1981-12-03 00:00:00   950          30
 7902 FORD    ANALYST   7566 1981-12-03 00:00:00  3000          20
 7934 MILLER  CLERK     7782 1982-01-23 00:00:00  1300          10

14 rows selected.

2.實驗目標
從上面的員工資訊表emp中得到以下六列資訊:
1)部門號
2)員工名字
3)工資
4)部門工資總數
5)員工工資佔所在部門總工資數的百分比
6)員工工資佔所總工資數的百分比

3.使用Oracle的分析函式實現如下
sec@ora10g> select deptno, ename, sal,
  2         sum(sal)
  3           over(partition by deptno order by sal, ename ) cum_sal,
  4         round(100*ratio_to_report(sal)
  5           over(partition by deptno),1) pct_dept,
  6         round(100*ratio_to_report(sal)
  7           over(),1) pct_overall
  8    from emp
  9   order by deptno,sal
 10  /

    DEPTNO ENAME             SAL    CUM_SAL   PCT_DEPT PCT_OVERALL
---------- ---------- ---------- ---------- ---------- -----------
        10 MILLER           1300       1300       14.9         4.5
        10 CLARK            2450       3750         28         8.4
        10 KING             5000       8750       57.1        17.2
        20 SMITH             800        800        7.4         2.8
        20 ADAMS            1100       1900       10.1         3.8
        20 JONES            2975       4875       27.4        10.2
        20 FORD             3000       7875       27.6        10.3
        20 SCOTT            3000      10875       27.6        10.3
        30 JAMES             950        950       10.1         3.3
        30 MARTIN           1250       2200       13.3         4.3
        30 WARD             1250       3450       13.3         4.3
        30 TURNER           1500       4950         16         5.2
        30 ALLEN            1600       6550         17         5.5
        30 BLAKE            2850       9400       30.3         9.8

14 rows selected.

如果上面的顯示效果不是很漂亮,可以使用break on在SQL*Plus中美化一下
sec@ora10g> break on deptno skip 1
sec@ora10g> select deptno, ename, sal,
  2         sum(sal)
  3           over(partition by deptno order by sal, ename ) cum_sal,
  4         round(100*ratio_to_report(sal)
  5           over(partition by deptno),1) pct_dept,
  6         round(100*ratio_to_report(sal)
  7           over(),1) pct_overall
  8    from emp
  9   order by deptno,sal
 10  /

    DEPTNO ENAME             SAL    CUM_SAL   PCT_DEPT PCT_OVERALL
---------- ---------- ---------- ---------- ---------- -----------
        10 MILLER           1300       1300       14.9         4.5
           CLARK            2450       3750         28         8.4
           KING             5000       8750       57.1        17.2

        20 SMITH             800        800        7.4         2.8
           ADAMS            1100       1900       10.1         3.8
           JONES            2975       4875       27.4        10.2
           FORD             3000       7875       27.6        10.3
           SCOTT            3000      10875       27.6        10.3

        30 JAMES             950        950       10.1         3.3
           MARTIN           1250       2200       13.3         4.3
           WARD             1250       3450       13.3         4.3
           TURNER           1500       4950         16         5.2
           ALLEN            1600       6550         17         5.5
           BLAKE            2850       9400       30.3         9.8


14 rows selected.

OK,這時的顯示效果是不是很直觀,類似於報表統計之後的效果,這就是SQL的魅力。

4.解釋一下上面用到的分析函式
1)DEPTNO,ENAME和SAL三列就不用多說了,僅僅是欄位的羅列而已。

2)CUM_SAL列使用的分析函式是:sum(sal) over(partition by deptno order by sal, ename )
功能:按照部門號將每一位員工的工資進行累加,最終得到這個部門的部門工資總數。

3)PCT_DEPT列使用到的分析函式是:round(100*ratio_to_report(sal) over (partition by deptno),1)
功能:按照部門號deptno對工資sal計算得到員工工資佔所在部門總工資數的百分比

4)PCT_OVERALL列使用的分析函式是:round(100*ratio_to_report(sal) over(),1) pct_overall
功能:over引數為空,表示計算單位是所有的資料,最終得到員工工資佔所總工資數的百分比


5.如果不用分析函式如何改寫上面的SQL語句呢?可以做如下的改寫
sec@ora10g> break on deptno skip 1
sec@ora10g> select emp.deptno, emp.ename, emp.sal,
  2         sum(emp4.sal) cum_sal,
  3         round(100*emp.sal/emp2.sal_by_dept,1) pct_dept,
  4         round(100*emp.sal/emp3.sal_overall,1) pct_overall
  5    from emp,
  6         (select deptno, sum(sal) sal_by_dept
  7            from emp
  8           group by deptno ) emp2,
  9         (select sum(sal) sal_overall
 10            from emp ) emp3,
 11         emp emp4
 12   where emp.deptno = emp2.deptno
 13     and emp.deptno = emp4.deptno
 14     and (emp.sal > emp4.sal or
 15            (emp.sal = emp4.sal and emp.ename >= emp4.ename))
 16   group by emp.deptno, emp.ename, emp.sal,
 17         round(100*emp.sal/emp2.sal_by_dept,1),
 18         round(100*emp.sal/emp3.sal_overall,1)
 19   order by deptno, sal
 20  /

    DEPTNO ENAME             SAL    CUM_SAL   PCT_DEPT PCT_OVERALL
---------- ---------- ---------- ---------- ---------- -----------
        10 MILLER           1300       1300       14.9         4.5
           CLARK            2450       3750         28         8.4
           KING             5000       8750       57.1        17.2

        20 SMITH             800        800        7.4         2.8
           ADAMS            1100       1900       10.1         3.8
           JONES            2975       4875       27.4        10.2
           FORD             3000       7875       27.6        10.3
           SCOTT            3000      10875       27.6        10.3

        30 JAMES             950        950       10.1         3.3
           MARTIN           1250       2200       13.3         4.3
           WARD             1250       3450       13.3         4.3
           TURNER           1500       4950         16         5.2
           ALLEN            1600       6550         17         5.5
           BLAKE            2850       9400       30.3         9.8


14 rows selected.

6.小結
經測試表明,使用分析函式在資料量增加的情況下,執行效率是呈線性降低的,但是使用group by的實現方式,執行效率是呈指數下降!(您可以參考一下【附】中的測試結果)
因此,分析函式的實現方式可以成百上千的提高SQL的效率。
雖然對分析函式的學習和接觸有一個過程,不過在對海量資料進行分析時,漫長的執行等待是誰都無法容忍的,這也是技術的魅力所在。


【附】分析函式與普通查詢方法的效能測試結果
第一次測試:測試記錄數為14條
分析函式耗時:Elapsed: 00:00:00.02
普通查詢耗時:Elapsed: 00:00:00.09

第二次測試:測試記錄數
1792條
分析函式耗時:Elapsed: 00:00:00.06
普通查詢耗時:Elapsed: 00:00:01.37

第三次測試:測試記錄數
14336條
分析函式耗時:Elapsed: 00:00:00.24
普通查詢耗時:Elapsed: 00:01:17.43

第四次試驗:測試記錄數
1835008條
分析函式耗時:Elapsed: 00:00:26.63
普通查詢耗時:當emp表的資料量達到近2百萬的數量級的時候,普通查詢方法已經無法返回結果,時間超過50031秒,而且時間還在累計,我無法等到這個結果的返回,手工cancel了。

-- The End --

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/519536/viewspace-615990/,如需轉載,請註明出處,否則將追究法律責任。

相關文章