1. 程式人生 > >oracle keep分析函式

oracle keep分析函式

一、keep函式介紹
keep是Oracle下的另一個分析函式,他的用法不同於通過over關鍵字指定的分析函式,可以用於這樣一種場合下:取同一個分組下以某個欄位排序後,對指定欄位取最小或最大的那個值。

從這個前提出發,我們可以看到其實這個目標通過一般的row_number分析函式也可以實現,即指定rn=1。但是,該函式無法實現同時獲取最大和最小值。或者說用first_value和last_value,結合row_number實現,但是該種方式需要多次使用分析函式,而且還需要套一層SQL。於是出現了keep。

語法:
min | max(column1) keep (dense_rank first | last order by column2) over (partion by column3);

最前是聚合函式,可以是min、max、avg、sum。。。
column1為要計算的列;
dense_rank first,dense_rank last為keep 函式的保留屬性,表示分組、排序結果集中第一個、最後一個;
解釋:返回按照column3分組後,按照column2排序的結果集中第一個或最後一個最小值或最大值column1。

二、keep函式分析:
 create table t (
 X VARCHAR2(10),
 Y DATE,
 Z NUMBER
 );
 
SQL> desc t
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 X                                                  VARCHAR2(10)
 Y                                                  DATE
 Z                                                  NUMBER
  
構建資料:
insert into t (x,y,z) values('a',date'2011-02-1',111);
insert into t (x,y,z) values('a',date'2011-02-1',222);
insert into t (x,y,z) values('a',date'2011-02-1',888);
insert into t (x,y,z) values('a',date'2011-03-1',333);
insert into t (x,y,z) values('a',date'2011-04-1',555);
insert into t (x,y,z) values('a',date'2011-04-1',666);
insert into t (x,y,z) values('b',date'2011-02-1',111);
insert into t (x,y,z) values('b',date'2011-02-1',222);
insert into t (x,y,z) values('b',date'2011-03-1',333);
insert into t (x,y,z) values('b',date'2011-04-1',555); 

SQL> select x,to_char(y,'yyyy-mm') y,z from t order by x,y,z;

X          Y                Z
---------- ------- ----------
a          2011-02        111
a          2011-02        222
a          2011-02        888
a          2011-03        333
a          2011-04        555
a          2011-04        666
b          2011-02        111
b          2011-02        222
b          2011-03        333
b          2011-04        555

10 rows selected.
SQL>
下面,我們來使用keep函式來看一些例子。

SQL>select x,
       min(z) keep(dense_rank first order by trunc(y,'mm')) first_min,
       min(z) keep(dense_rank last order by trunc(y,'mm')) last_min,
       max(z) keep(dense_rank first order by trunc(y,'mm')) first_max,
       max(z) keep(dense_rank last order by trunc(y,'mm')) last_max
   from t
  group by x;

X           FIRST_MIN   LAST_MIN  FIRST_MAX   LAST_MAX
---------- ---------- ---------- ---------- ----------
a                 111        555        888        666
b                 111        555        222        555
這是顯示的效果。
我們來分析一下,SQL裡面的group by 是作用於聚集函式的,可以這麼理解一下。還有,這裡我們頭腦裡應該有這麼一個概念,排序即分組。

結合結果集與keep函式裡order by 子句,我們來看一下表中的資料,然後把表裡的資料進行如下劃分,以X為維度,按值a進行劃分:
a 2011-02 111
a 2011-02 222
a 2011-02 888
————————————— 這些資料劃分為第一組。
因為order by 是按日期(截止到月)進行排序的,上面的日期都一樣。
a 2011-03 333
—————————————- 這些資料劃分為第二組。
a 2011-04 555
a 2011-04 666
—————————————- 這些資料劃分為第三組。
以X為維度,按值b進行劃分:
b 2011-02 111
b 2011-02 222
—————————————– 第一組
b 2011-03 333
—————————————- 第二組
b 2011-04 555
—————————————- 第三組

好,有了上述的劃分,我們就不難理解SQL輸出的結果集了。下面我們來分析一下SQL中輸入的值。結合SQL

min(z) keep(dense_rank first order by trunc(y,'mm')) first_min 該子句用來顯示,以a為維度,以y為序,顯示第一組(因為keep函式裡指定是的first子句)中的最小值,所以,它顯示為111(結合上述的劃分結果來看)。
min(z) keep(dense_rank last order by trunc(y,'mm')) last_min 該子句用來顯示,以a為維度,以y為序,顯示最後一組(因為keep函式裡指定是的last子句)中的最小值,所以,這顯示為555。
下面,我們來再分析一下以X為b的結果。
我們來看一下x=b的那一行,last_min與last_max的值一樣。這是因為x為b的最後一組,它只有一行資料,對於一行資料而言,它的最大值與最小值肯定是同一個值了,所以last_min與last_max一樣。

使用其他聚合函式也非常好理解了,比如我使用sum函式。
SQL> select x, 
   sum(z) keep(dense_rank first order by trunc(y,'mm')) first_sum,
   sum(z) keep(dense_rank last order by trunc(y,'mm'))  last_sum
 from t
 group by x;

X           FIRST_SUM   LAST_SUM
---------- ---------- ----------
a                1221       1221
b                 333        555

三、例項
1、按照某欄位分組、然後排序、去最值
建立表、插入資料

create table tx2(id1 int ,id2 int,id3 int);

insert into tx2(id1,id2,id3) values(1,111,1);
insert into tx2(id1,id2,id3) values(1,222,1);
insert into tx2(id1,id2,id3) values(1,333,2);
insert into tx2(id1,id2,id3) values(1,444,3);
insert into tx2(id1,id2,id3) values(2,555,1);
insert into tx2(id1,id2,id3) values(2,666,2);
insert into tx2(id1,id2,id3) values(2,777,3);

查詢:
SQL> select * from tx2;

       ID1  ID2     ID3
---------- ---------- ----------
1  111       1
1  222       1
1  333       2
1  444       3
2  555       1
2  666       2
2  777       3

7 rows selected.
按照ID1分組,ID3排序後,第一個最小的ID2:

 select t.id1,t.id2,t.id3,min(t.id2)keep(dense_rank first order by t.id3)over(partition by t.id1) minid2 from tx2 t;

       ID1  ID2     ID3     MINID2
---------- ---------- ---------- ----------
1  111       1111
1  222       1111
1  333       2111
1  444       3111
2  555       1555
2  666       2555
2  777       3555

7 rows selected.

2、統計部門最高、低工資

SQL> SELECT t.empno,t.ename,t.mgr,t.sal,t.deptno  FROM emp t  ORDER BY t.sal,  t.deptno;  

     EMPNO ENAME     MGRSALDEPTNO
---------- ---------- ---------- ---------- ----------
      7369 SMITH    7902800    20
      7900 JAMES    7698950    30
      7876 ADAMS    7788       1100    20
      7521 WARD     7698       1250    30
      7654 MARTIN    7698       1250    30
      7934 MILLER    7782       1300    10
      7844 TURNER    7698       1500    30
      7499 ALLEN    7698       1600    30
      7782 CLARK    7839       2450    10
      7698 BLAKE    7839       2850    30
      7566 JONES    7839       2975    20
     EMPNO ENAME     MGRSALDEPTNO
---------- ---------- ---------- ---------- ----------
      7788 SCOTT    7566       3000    20
      7902 FORD     7566       3000    20
      7839 KING        5000    10

14 rows selected.

現在要查詢表中工資最高的部門號的最大最小值,工資最低的部門號的最大最小值 :
SQL> SELECT MIN(t.deptno) KEEP(DENSE_RANK FIRST ORDER BY t.sal) a,
MAX(t.deptno) KEEP(DENSE_RANK FIRST ORDER BY t.sal) b,
MIN(t.deptno) KEEP(DENSE_RANK LAST ORDER BY t.sal) c,
MAX(t.deptno) KEEP(DENSE_RANK LAST ORDER BY t.sal) d
  5  FROM emp t;  

A    B       C  D
---------- ---------- ---------- ----------
20   20      10 10

加上over,對每一行記錄做計算,看看效果:

SQL> SELECT t.empno,t.ename,t.mgr,t.sal,t.deptno,
MIN(t.deptno) KEEP(DENSE_RANK FIRST ORDER BY t.sal)over() a,
MAX(t.deptno) KEEP(DENSE_RANK FIRST ORDER BY t.sal)over() b,
MIN(t.deptno) KEEP(DENSE_RANK LAST ORDER BY t.sal)over() c,
MAX(t.deptno) KEEP(DENSE_RANK LAST ORDER BY t.sal)over() d
  6  FROM emp t;  

     EMPNO ENAME     MGRSALDEPTNOA   B      C D
---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
      7369 SMITH    7902800    20       20  20     10 10
      7499 ALLEN    7698       1600    30       20  20     10 10
      7521 WARD     7698       1250    30       20  20     10 10
      7566 JONES    7839       2975    20       20  20     10 10
      7654 MARTIN    7698       1250    30       20  20     10 10
      7698 BLAKE    7839       2850    30       20  20     10 10
      7782 CLARK    7839       2450    10       20  20     10 10
      7788 SCOTT    7566       3000    20       20  20     10 10
      7839 KING        5000    10       20  20     10 10
      7844 TURNER    7698       1500    30       20  20     10 10
      7876 ADAMS    7788       1100    20       20  20     10 10

     EMPNO ENAME     MGRSALDEPTNOA   B      C D
---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
      7900 JAMES    7698950    30       20  20     10 10
      7902 FORD     7566       3000    20       20  20     10 10
      7934 MILLER    7782       1300    10       20  20     10 10

14 rows selected.

下面對每一個mgr求最大(最小)工資的部門號的最大(最小)值 :
  
轉自:http://lanjingling.github.io/2015/10/09/oracle-fenxihanshu-3/,並稍作修改。