1. 程式人生 > >ORACLE進階之三:分析函式

ORACLE進階之三:分析函式

有時候我們需要從DB中提取一些很複雜的資料,而標準SQL卻對此無能為力,或者是執行效率非常的低;比如我們需要提取如下資料:
    逐行顯示各個部門的累計工資,每行包括部門內前面所有人的工資總和;
    查詢各個部門工資最高的前N個人;
    ……

語法
 Function名稱([引數]) OVER ([partition 子句][ order 子句] [window 子句])
  OVER為分析函式的關鍵字,用於區別普通的聚合函式;從語法格式上區分的話,沒加over()即時聚合函式,加了over()就是分析函式。
 Partition 子句:Partition by exp1[ ,exp2]...;

  主要用於分組,可以理解成select中的group by;不過它跟select語句後跟的group by 子句並不衝突;指定該子句之後,前面的函式起效範圍就是該分組內,若不指定,則Function的起效範圍是全部結果集。
 Order 子句:Order by exp1[asc|desc] [ ,exp2 [asc|desc]]... [nulls first|last];
  其引數基本與select中的order by相同;Nulls first|last是用來限定nulls在分組序列中的所在位置的,我們知道oracle中對於null的定義是未知,所以預設order by的時候nulls總會被排在最前面。如果想控制值為null的行顯示位置,nulls first|last引數就能派上用場了。
 Window 子句
:該子句的語法比較複雜,具體可以見下圖;

  該子句給出了一個定義變化或者固定的資料視窗方法,分析函式將對這些資料進行操作;預設情況下,一般用不上該子句,分析函式產生一個固定的視窗,影響的資料範圍是從第一行到當前行,其效果和RANGE BETWEEN UNDOUNDED PRECEDING AND CURRENT ROW一樣;若需要指定操作資料為當前行及其前兩行,則可以用ROWS 2 PRECEDING來實現其效果;
 其中用[]標註的子句都可以為空,一個最簡單的分析函式可能是COUNT(*) OVER ();

樣例
逐行顯示各個部門的累計工資,每行包括部門內前面所有人的工資總和:
SELECT EMP_NO,
       NAME,
       DEPT_NO,
       SUM(SAL) OVER(PARTITION BY DEPT_NO ORDER BY EMP_NO) DEPT_SAL_SUM
  FROM EMP
 ORDER BY DEPT_NO, EMP_NO;

查詢各個部門工資最高的前N個人:
SELECT *
  FROM (SELECT DEPT_NO,
               NAME,
               SAL,
               DENSE_RANK() OVER(PARTITION BY DEPT_NO ORDER BY SAL DESC) DR
          FROM EMP)
 WHERE DR <= 3
 ORDER BY DEPT_NO, SAL DESC;

注意點
1、分析函式與聚合函式非常相似,不同於聚合函式的地方在於它們每個分組序列均返回多行,而聚合函式返回一行;
2、帶有分析函式的SQL列表中,除了order by子句之外,分析函式將在SQL語句中最後執行;因此,分析函式只能用於select的列或order by子句,而不能用於where、group by、having之類的語句中;
3、當分析函式中使用了distinct引數時,則只能使用partition子句,而不能指定order by子句;
4、SELECT語句中的ORDER BY子句與分析函式中的order by子句是互不影響的,但一般來說兩者一致比較好,若兩者不一致,則意味著分析函式需要對結果集進行多次排序,這將嚴重降低分析函式的執行效率;
5、dense_rank在做排序時如果遇到列有重複值,則重複值所在行的序列值相同,而其後的序列值依舊遞增,rank則是重複值所在行的序列值相同,但其後的序列值從+重複行數開始遞增,而row_number則不管是否有重複行,序列值始終遞增;

函式列表
大致有26個函式可用,其中很多都是和聚合函式同名的,比如SUM、AVG、MIN、MAX……;其他是一些提供新功能的新函式;具體的函式列表如下: