Oracle listagg函數、lag函數、lead函數

原文:http://blog.sina.com.cn/s/blog_4cef5c7b01016efp.html

 

Listagg函數

我們有時候會遇到這樣的需求:“對員工列表進行操作,將每個部門的員工名稱橫向排列,以逗號進行分割”。

員工表我們使用scott用戶schema下的emp表。

SQL> select * from emp;
 
EMPNO ENAME       JOB        MGR   HIREDATE    SAL        COMM      DEPTNO
----- ---------- --------- ----- ----------- --------- --------- ------
7369  SMITH       CLERK     7902   1980-12-17  800.00               20
7876  ADAMS       CLERK     7788   1987-5-23   1100.00              20
7900  JAMES       CLERK     7698   1981-12-3   950.00               30
7902  FORD        ANALYST   7566   1981-12-3   3000.00              20
7934  MILLER      CLERK     7782   1982-1-23   1300.00              10
  
(篇幅原因,有省略……)
 
14 rows selected

這個需求的關鍵在於如何將ename員工名稱列壓扁爲一行數據。如果不使用SQL解決,最直觀的想法就是使用PL/SQL進行迭代遍歷,獲取到所有的數據行記錄。

此時,我們就可以求助Oracle 11g中的函數listagg。首先我們來看一下listagg的函數描述(摘自Oracle SQL Reference)。

listagg的作用是將分組範圍內的所有行特定列的記錄加以合併成行。函數簽名中的measure_expr爲分組中每個列的表達式,而delimiter爲合併分割符。如果delimiter不設置的話,就表示無分割符。

中間within group後面的order_by_clause表示的是進行合併中要遵守的排序順序。而後面的over子句表明listagg是具有分析函數analyze funcation特性的。具體採用listagg有三個場景。


當無分組的single-list情況下

如果要獲取到deptno爲30的所有員工橫行記錄。

SQL> select * from emp where deptno=30;
 
EMPNO ENAME      JOB       MGR   HIREDATE    SAL       COMM      DEPTNO
----- ---------- --------- ----- ----------- --------- --------- ------
7499  ALLEN      SALESMAN  7698  1981-2-20   1600.00   300.00    30
7521  WARD       SALESMAN  7698  1981-2-22   1250.00   500.00    30
7654  MARTIN     SALESMAN  7698  1981-9-28   1250.00   1400.00   30
7698  BLAKE      MANAGER   7839  1981-5-1    2850.00             30
7844  TURNER     SALESMAN  7698  1981-9-8    1500.00   0.00      30
7900  JAMES      CLERK     7698  1981-12-3   950.00              30
 
6 rows selected
 
--按照empno進行排序
SQL> select listagg(ename, ',') within group (order by empno) from emp where deptno=30;

LISTAGG(ENAME,',')WITHINGROUP(
------------------------------------------------------------
ALLEN , WARD , MARTIN , BLAKE , TURNER , JAMES


在有分組條件下的listagg使用

如果要使用分組統計各個部門的所有員工列表。

SQL> select deptno, listagg(ename,' ,') within group (order by empno) from emp group by deptno;

DEPTNO LISTAGG(ENAME,',')WITHINGROUP(
------ -------------------------------------
10     CLARK ,KING ,MILLER
20     SMITH ,JONES ,SCOTT ,ADAMS ,FORD
30     ALLEN ,WARD ,MARTIN ,BLAKE ,TURNER ,JAMES


使用over分組情況

如果要統計所有工作十年以上員工和他們相同部門的員工信息,就需要在listagg的基礎上加入over分析函數子句。

SQL> select deptno, ename, listagg(ename, ' , ') within group (order by empno)
 2 over (partition by deptno) as emp_list
 3 from emp
 4 where hiredate<=add_months(sysdate,-10*12);

DEPTNO ENAME     EMP_LIST
------ --------- ------------------
10     CLARK     CLARK , KING , MILLER
10     KING      CLARK , KING , MILLER
10     MILLER    CLARK , KING , MILLER
20     SMITH     SMITH , JONES , SCOTT , ADAMS , FORD
20     JONES     SMITH , JONES , SCOTT , ADAMS , FORD
20     SCOTT     SMITH , JONES , SCOTT , ADAMS , FORD
20     ADAMS     SMITH , JONES , SCOTT , ADAMS , FORD
20     FORD      SMITH , JONES , SCOTT , ADAMS , FORD
30     ALLEN     ALLEN , WARD , MARTIN , BLAKE , TURNER , JAMES
30     WARD      ALLEN , WARD , MARTIN , BLAKE , TURNER , JAMES
30     MARTIN    ALLEN , WARD , MARTIN , BLAKE , TURNER , JAMES
30     BLAKE     ALLEN , WARD , MARTIN , BLAKE , TURNER , JAMES
30     TURNER    ALLEN , WARD , MARTIN , BLAKE , TURNER , JAMES
30     JAMES     ALLEN , WARD , MARTIN , BLAKE , TURNER , JAMES

14 rows selected


lag函數“取到上個月的銷售額” 

我們在進行銷售數據統計彙總時候,經常遇到這樣的需求:“對比上月(上季度同月份或者上年度同月份),我們的銷售變化情況如何?”。我們的銷售數據通常是對應單月信息,如下所示。

SQL> select * from sales_qual;

MONT       QUALITIES   PRICE
---------- ----------- ------
2011-01    1000        23.40
2011-02    1020        23.40
2011-03    1030        33.40
2011-04    1035        10.30

如果要獲取到之前月份的信息,沒有SQL專門函數就意味着需要使用PL/SQL代碼進行反覆的迭代獲取。現在,我們可以使用lag函數來輕易實現這個功能。

lag函數是一個典型的分析函數。它提供了在不使用自連接的情況下,訪問多個數據行的能力。在返回多個結果行的時候,lag函數可以訪問到向上特定offset偏移行的數據。

value_expr就是訪問到向上數據行進行的操作。offset是返回偏移的函數,默認值爲1。over中,可以定義內部分析的順序列。

如果我們要獲取到對應上個月的銷售數據,SQL語句如下:

SQL> select mont, qualities, lag(qualities,1) over (order by mont) as "Next Month Qual"
 2 from sales_qual
 3 order by mont;

MONT       QUALITIES   Next Month Qual
---------- ----------- ---------------
2011-01    1000
2011-02    1020        1000
2011-03    1030        1020
2011-04    1035        1030

之後對銷量變化率的處理就方便了,可以進行增長率比對等操作。那麼,如果是上一年度或者上一季度的數據呢?我們只需要調節offset,從1變化爲12或者3就可以了。

最後,對ignore/respect nulls子句的使用是什麼呢?該子句的作用是確定當value_expr表達式計算出的數值爲空null的時候,該列如何進行計算。ignore nulls的作用就是忽略上面計算爲空的行,採用上上行row的計算結果。respect nulls的作用是直接反映爲null。respect nulls爲默認值。

SQL> select * from sales_qual;

MONT       QUALITIES   PRICE
---------- ----------- ------
......
2011-04    1035        10.30
2011-05                12.30
2011-06               

6 rows selected

SQL> select mont, qualities, lag(qualities, 1) ignore nulls over (order by mont) as "Next Month Qual"
 2 from sales_qual
 3 order by mont;

MONT       QUALITIES   Next Month Qual
---------- ----------- ---------------
......
2011-04    1035        1030
2011-05                1035
2011-06                1035
6 rows selected

SQL> select mont,qualities, lag(qualities,1) respect nulls over (order by mont) as "Next Month Qual"
 2 from sales_qual
 3 order by mont;

MONT       QUALITIES   Next Month Qual
---------- ----------- ---------------
......
2011-04    1035        1030
2011-05                1035
2011-06               

6 rows selected


lead函數獲取下一個月銷售量

有lag的獲取上個offset處理行的函數,就有lead函數處理下一個處理行的函數。lead函數實際上就是lag的逆向過程。


相關各項參數與lag函數的相同。區別就在於lead函數獲取的是排序後結果集合的後offset數據行記錄。

SQL> select mont,qualities, lead(qualities,1) over (order by mont) as "Next Month Qual"
 2 from sales_qual
 3 order by mont;
 
MONT        QUALITIES Next Month Qual
---------- ----------- ---------------
2011-01          1000           1020
2011-02          1020           1030
2011-03          1030           1035
2011-04          1035


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章