1) SQL analytic function
1@@@@ basic example
@@@
@@@<1> over() for each record, or for each group
@@@
SCOTT@ocm> !cat tmp.sql
set echo off
set feedback off
set verify off
prompt
prompt Without OVER(), aggregate function
prompt   one record for each group
SELECT  COUNT(*) 
      , SUM(sal)
  FROM emp
 WHERE deptno = 30
/
SELECT  deptno
      , COUNT(*)
      , SUM(sal)
  FROM emp
 GROUP BY deptno
/
prompt
prompt With OVER(), analytic function
prompt   one result for each record in the dataset, no grouping
SELECT  deptno, ename, sal
      , COUNT(*)  OVER ()
      , SUM(sal)  OVER ()
  FROM emp
 WHERE deptno = 30
/



SCOTT@ocm> @tmp.sql

Without OVER(), aggregate function
one record for each group

  COUNT(*)   SUM(SAL)
---------- ----------
     6     9400

    DEPTNO   COUNT(*)    SUM(SAL)
---------- ---------- ----------
    30        6        9400
    20        5       10875
    10        3        8750

With OVER(), analytic function
one result for each record in the dataset, no grouping

    DEPTNO ENAME         SAL COUNT(*)OVER() SUM(SAL)OVER()
---------- ---------- ---------- -------------- --------------
    30 ALLEN        1600          6       9400
    30 WARD         1250          6       9400
    30 MARTIN        1250          6       9400
    30 BLAKE        2850          6       9400
    30 TURNER        1500          6       9400
    30 JAMES         950          6       9400


@@@
@@@<2> Group by OVER()
@@@
SCOTT@ocm> !cat tmp.sql
set echo off
set feedback off
set verify off
prompt @@@ Distinct versus Group By
prompt
prompt Using GROUP BY for each group
SELECT  deptno
      , COUNT(*) OVER() AS emp_counter
  FROM scott.emp
 GROUP BY deptno
/
prompt
prompt Without GROUP BY for each record
SELECT  DISTINCT deptno
      , COUNT(*) OVER() AS emp_counter
  FROM scott.emp
/
SELECT  deptno
      , COUNT(*) OVER() AS emp_counter
  FROM scott.emp
/
SELECT COUNT(*)
  FROM scott.emp
/

SCOTT@ocm> @tmp.sql
@@@ Distinct versus Group By

Using GROUP BY for each group

    DEPTNO EMP_COUNTER
---------- -----------
    10         3
    20         3
    30         3

Without GROUP BY for each record

    DEPTNO EMP_COUNTER
---------- -----------
    30        14
    10        14
    20        14

    DEPTNO EMP_COUNTER
---------- -----------
    20        14
    30        14
    30        14
    20        14
    30        14
    30        14
    10        14
    20        14
    10        14
    30        14
    20        14
    30        14
    20        14
    10        14

  COUNT(*)
----------
    14



2@@@@ partition by
Within the set of parentheses
Expressions telling the function to calculate differently
Three possible components
  Partition
  Order
  Windowing
Some or all are optional, depending upon the function
Components must be in this order

@@@
@@@<1> partition by clause
@@@
SCOTT@ocm> !cat tmp.sql
set echo off
set feedback off
set verify off
prompt @@@ partition by
prompt   calculated on a subset of the records
SELECT  deptno, ename, sal, job
      , COUNT(*) OVER(PARTITION BY job)    job_count
      , SUM(sal) OVER(PARTITION BY deptno) dept_sum_sal
  FROM scott.emp
 WHERE rownum < 5
/
SELECT  deptno, ename, sal, job
      , COUNT(*) OVER(PARTITION BY job)    job_count
      , SUM(sal) OVER(PARTITION BY deptno) dept_sum_sal
  FROM scott.emp
 WHERE rownum < 8
/
SELECT  deptno, ename, sal, job
      , COUNT(*) OVER(PARTITION BY job)    job_count
      , SUM(sal) OVER(PARTITION BY deptno) dept_sum_sal
  FROM scott.emp
/


SCOTT@ocm> @tmp.sql
@@@ partition by
calculated on a subset of the records
    DEPTNO ENAME         SAL JOB        JOB_COUNT DEPT_SUM_SAL
---------- ---------- ---------- --------- ---------- ------------
    20 SMITH         800 CLERK            1          3775
    20 JONES        2975 MANAGER        1          3775
    30 WARD         1250 SALESMAN        2          2850
    30 ALLEN        1600 SALESMAN        2          2850

    DEPTNO ENAME         SAL JOB        JOB_COUNT DEPT_SUM_SAL
---------- ---------- ---------- --------- ---------- ------------
    20 SMITH         800 CLERK            1          3775
    30 BLAKE        2850 MANAGER        3          6950
    20 JONES        2975 MANAGER        3          3775
    10 CLARK        2450 MANAGER        3          2450
    30 MARTIN        1250 SALESMAN        3          6950
    30 WARD         1250 SALESMAN        3          6950
    30 ALLEN        1600 SALESMAN        3          6950

    DEPTNO ENAME         SAL JOB        JOB_COUNT DEPT_SUM_SAL
---------- ---------- ---------- --------- ---------- ------------
    20 SCOTT        3000 ANALYST        2         10875
    20 FORD         3000 ANALYST        2         10875
    10 MILLER        1300 CLERK            4          8750
    20 SMITH         800 CLERK            4         10875
    30 JAMES         950 CLERK            4          9400
    20 ADAMS        1100 CLERK            4         10875
    10 CLARK        2450 MANAGER        3          8750
    30 BLAKE        2850 MANAGER        3          9400
    20 JONES        2975 MANAGER        3         10875
    10 KING         5000 PRESIDENT        1          8750
    30 MARTIN        1250 SALESMAN        4          9400
    30 WARD         1250 SALESMAN        4          9400
    30 TURNER        1500 SALESMAN        4          9400
    30 ALLEN        1600 SALESMAN        4          9400

@@@Summary:
  First, analytic function for each record.
  Here, COUNT(*) OVER(PARTITION BY job),  total number of the same job in
  current results set.
  Here, COUNT(*) OVER(PARTITION BY deptno), total salary of the department
  in current record  


@@@
@@@<2> use correlated scalar subqueries
@@@
SCOTT@ocm> !cat tmpx.sql
set echo off
set verify off
set feedback off
set autot off
prompt overwritted by normal sql
SELECT deptno, ename, sal, job
     , (SELECT COUNT(*) FROM emp WHERE job=e.job) job_count
     , (SELECT SUM(sal) FROM emp WHERE deptno=e.deptno) dept_sum_sal
  FROM emp e
/

SCOTT@ocm> @tmpx.sql
overwritted by normal sql
    DEPTNO ENAME         SAL JOB        JOB_COUNT DEPT_SUM_SAL
---------- ---------- ---------- --------- ---------- ------------
    20 SMITH         800 CLERK            4         10875
    30 ALLEN        1600 SALESMAN        4          9400
    30 WARD         1250 SALESMAN        4          9400
    20 JONES        2975 MANAGER        3         10875
    30 MARTIN        1250 SALESMAN        4          9400
    30 BLAKE        2850 MANAGER        3          9400
    10 CLARK        2450 MANAGER        3          8750
    20 SCOTT        3000 ANALYST        2         10875
    10 KING         5000 PRESIDENT        1          8750
    30 TURNER        1500 SALESMAN        4          9400
    20 ADAMS        1100 CLERK            4         10875
    30 JAMES         950 CLERK            4          9400
    20 FORD         3000 ANALYST        2         10875
    10 MILLER        1300 CLERK            4          8750


@@@
@@@<3> Observe the execution plan 
@@@
SCOTT@ocm> !cat tmpx.sql
set echo off
set verify off
set feedback off
set autot traceonly explain 
prompt observe the execution plan
SELECT deptno, ename, sal, job
     , (SELECT COUNT(*) FROM emp WHERE job=e.job) job_count
     , (SELECT SUM(sal) FROM emp WHERE deptno=e.deptno) dept_sum_sal
  FROM emp e
/
SELECT deptno, ename, sal, job
     , COUNT(*) OVER(PARTITION BY job)    job_count
     , SUM(sal) OVER(PARTITION BY deptno) dept_sum_sal
  FROM emp e
/

SCOTT@ocm> @tmpx.sql
observe the execution plan

Execution Plan
----------------------------------------------------------
Plan hash value: 1174980467

---------------------------------------------------------------------------
| Id  | Operation       | Name | Rows  | Bytes | Cost (%CPU)| Time      |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |    14 |   294 |    3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |    1 |    8 |           |      |
|*  2 |   TABLE ACCESS FULL| EMP  |    3 |    24 |    3   (0)| 00:00:01 |
|   3 |  SORT AGGREGATE    |      |    1 |    7 |           |      |
|*  4 |   TABLE ACCESS FULL| EMP  |    5 |    35 |    3   (0)| 00:00:01 |
|   5 |  TABLE ACCESS FULL | EMP  |    14 |   294 |    3   (0)| 00:00:01 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("JOB"=:B1)
   4 - filter("DEPTNO"=:B1)
 

Execution Plan
----------------------------------------------------------
Plan hash value: 4086863039

----------------------------------------------------------------------------
| Id  | Operation        | Name | Rows  | Bytes | Cost (%CPU)| Time       |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |       |    14 |   294 |     5  (40)| 00:00:01 |
|   1 |  WINDOW SORT        |       |    14 |   294 |     5  (40)| 00:00:01 |
|   2 |   WINDOW SORT        |       |    14 |   294 |     5  (40)| 00:00:01 |
|   3 |    TABLE ACCESS FULL| EMP  |    14 |   294 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------

@@@Summary:
   Traditional aggregate function occurs 3 pass (full table scan) over the table
   Analytic SQL only 1 pass(full table scan).


3@@@@ LEAD and LAG
@@@
@@@<1> basic LAG and LEAD
@@@
Usage:
  LAG/LEAD ( field_name, num_recs ) OVER ( )
  Return the value from a field when looking one record (or more) behind/ahead
    Using the order specified
    ORDER BY is required
  Does not have to be order used in the query
  Optional second param to look more than one record
  These functions are analytic only

SCOTT@ocm> !cat tmp.sql
set echo off
set feedback off
set verify off
set autot off
prompt @@@LAG|LEAD order by hiredate, in the same record display the
prompt ~  ename which order is prior current record in queue order
prompt ~  by analytic function LAG. LEAD
SELECT  deptno, ename, to_char(hiredate,'yyyy-mm-dd')
      , LAG(ename) OVER(ORDER BY hiredate) prior_hire
  FROM scott.emp
 ORDER BY deptno, ename
/   
SELECT  deptno, ename, to_char(hiredate,'yyyy-mm-dd')
      , LEAD(ename) OVER(ORDER BY hiredate) behind_hire
  FROM scott.emp
 ORDER BY deptno, ename
/   
prompt
prompt @@@Compare below output, observe the ename and *_hire fields
prompt ~  above 
SELECT ename, to_char(hiredate,'yyyy-mm-dd')
  FROM scott.emp
ORDER BY hiredate
/


SCOTT@ocm> @tmp.sql
@@@LAG|LEAD order by hiredate, in the same record display the
~  ename which order is prior current record in queue order
~  by analytic function LAG. LEAD

    DEPTNO ENAME      TO_CHAR(HI PRIOR_HIRE
---------- ---------- ---------- ----------
    10 CLARK      1981-06-09 BLAKE
    10 KING       1981-11-17 MARTIN
    10 MILLER     1982-01-23 FORD
    20 ADAMS      1987-05-23 SCOTT
    20 FORD       1981-12-03 JAMES
    20 JONES      1981-04-02 WARD
    20 SCOTT      1987-04-19 MILLER
    20 SMITH      1980-12-17
    30 ALLEN      1981-02-20 SMITH
    30 BLAKE      1981-05-01 JONES
    30 JAMES      1981-12-03 KING
    30 MARTIN     1981-09-28 TURNER
    30 TURNER     1981-09-08 CLARK
    30 WARD       1981-02-22 ALLEN

    DEPTNO ENAME      TO_CHAR(HI BEHIND_HIR
---------- ---------- ---------- ----------
    10 CLARK      1981-06-09 TURNER
    10 KING       1981-11-17 JAMES
    10 MILLER     1982-01-23 SCOTT
    20 ADAMS      1987-05-23
    20 FORD       1981-12-03 MILLER
    20 JONES      1981-04-02 BLAKE
    20 SCOTT      1987-04-19 ADAMS
    20 SMITH      1980-12-17 ALLEN
    30 ALLEN      1981-02-20 WARD
    30 BLAKE      1981-05-01 CLARK
    30 JAMES      1981-12-03 FORD
    30 MARTIN     1981-09-28 KING
    30 TURNER     1981-09-08 MARTIN
    30 WARD       1981-02-22 JONES

@@@Compare below output, observe the ename and *_hire fields
~  above

ENAME       TO_CHAR(HI
---------- ----------
SMITH       1980-12-17
ALLEN       1981-02-20
WARD       1981-02-22
JONES       1981-04-02
BLAKE       1981-05-01
CLARK       1981-06-09
TURNER       1981-09-08
MARTIN       1981-09-28
KING       1981-11-17
JAMES       1981-12-03
FORD       1981-12-03
MILLER       1982-01-23
SCOTT       1987-04-19
ADAMS       1987-05-23


@@@
@@@<2> ORDER BY variation
@@@
  In this example,
  LAG(ename,2) means it prior emp name with interval 2, absoultly, it would
  be null.
  LEAD(ename) with DESC, like negative negative = positive

SCOTT@ocm> !cat tmp.sql
set echo off
set feedback off
set verify off
set autot off
prompt @@@ORDER BY variation
SELECT  deptno, ename, sal
      , LAG(ename)   OVER(ORDER BY ename)      f1
      , LAG(ename,2) OVER(ORDER BY ename)      f2
      , LEAD(ename)  OVER(ORDER BY ename DESC) f3
      , LAG(sal)     OVER(ORDER BY ename)      f4
  FROM scott.emp
 ORDER BY deptno, ename
/   
prompt
prompt @@@Compare below output, observe the ename and *_hire fields
prompt ~  above 
SELECT  ename
  FROM scott.emp
ORDER BY ename
/


SCOTT@ocm> @tmp.sql
@@@ORDER BY variation

    DEPTNO ENAME         SAL F1        F2           F3          F4
---------- ---------- ---------- ---------- ---------- ---------- ----------
    10 CLARK        2450 BLAKE        ALLEN      BLAKE        2850
    10 KING         5000 JONES        JAMES      JONES        2975
    10 MILLER        1300 MARTIN     KING       MARTIN        1250
    20 ADAMS        1100
    20 FORD         3000 CLARK        BLAKE      CLARK        2450
    20 JONES        2975 JAMES        FORD       JAMES         950
    20 SCOTT        3000 MILLER     MARTIN     MILLER        1300
    20 SMITH         800 SCOTT        MILLER     SCOTT        3000
    30 ALLEN        1600 ADAMS               ADAMS        1100
    30 BLAKE        2850 ALLEN        ADAMS      ALLEN        1600
    30 JAMES         950 FORD        CLARK      FORD        3000
    30 MARTIN        1250 KING        JONES      KING        5000
    30 TURNER        1500 SMITH        SCOTT      SMITH         800
    30 WARD         1250 TURNER     SMITH      TURNER        1500

@@@Compare below output, observe the ename and *_hire fields
~  above

ENAME
----------
ADAMS
ALLEN  -----then this one is prior record with interval 2
BLAKE
CLARK  -----if this one is current record
FORD
JAMES  -----the same this one is behind record with interval 2
JONES
KING
MARTIN
MILLER
SCOTT
SMITH
TURNER
WARD


@@@
@@@<3> ORDER BY and PARTITION BY clause
@@@
SCOTT@ocm> !cat tmp.sql
set echo off
set feedback off
set verify off
set autot off
prompt @@@ORDER BY and PARTITION BY variation
prompt ~  PARTITION BY deptno means treat a partition as a result sets
prompt ~  so the null would add.
SELECT  deptno, ename, sal
      , LAG(ename) OVER(ORDER BY ename)                         f1
      , LAG(ename) OVER(PARTITION BY deptno ORDER BY ename)     f2
      , LAG(ename) OVER(PARTITION BY deptno ORDER BY sal DESC)  f3
  FROM scott.emp
 ORDER BY deptno, ename
/   
prompt
prompt @@@Compare below output, observe the ename and *_hire fields
prompt ~  above 
SELECT  deptno, ename
  FROM scott.emp
ORDER BY deptno, ename
/
SELECT  ename, deptno, sal
  FROM scott.emp
ORDER BY sal
/


SCOTT@ocm> @tmp.sql
@@@ORDER BY and PARTITION BY variation
~  PARTITION BY deptno means treat a partition as a result sets
~  so the null would add.

    DEPTNO ENAME         SAL F1        F2           F3
---------- ---------- ---------- ---------- ---------- ----------
    10 CLARK        2450 BLAKE               KING
    10 KING         5000 JONES        CLARK
    10 MILLER        1300 MARTIN     KING       CLARK
    20 ADAMS        1100               JONES
    20 FORD         3000 CLARK        ADAMS      SCOTT
    20 JONES        2975 JAMES        FORD       FORD
    20 SCOTT        3000 MILLER     JONES
    20 SMITH         800 SCOTT        SCOTT      ADAMS
    30 ALLEN        1600 ADAMS               BLAKE
    30 BLAKE        2850 ALLEN        ALLEN
    30 JAMES         950 FORD        BLAKE      WARD
    30 MARTIN        1250 KING        JAMES      TURNER
    30 TURNER        1500 SMITH        MARTIN     ALLEN
    30 WARD         1250 TURNER     TURNER     MARTIN

@@@Compare below output, observe the ename fields
~  above

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

ENAME           DEPTNO         SAL
---------- ---------- ----------
SMITH           20         800
JAMES           30         950
ADAMS           20        1100
WARD           30        1250
MARTIN           30        1250
MILLER           10        1300
TURNER           30        1500
ALLEN           30        1600
CLARK           10        2450
BLAKE           30        2850
JONES           20        2975
SCOTT           20        3000
FORD           20        3000
KING           10        5000



4@@@@Ordering ( Ranking ) functions: RANK, DENSE_RANK, ROW_NUMBER
Usage:
  RANK() | DENSE_RANK | ROW_NUMBER OVER(ORDER BY field_name)
  Where does this record fall, when the records are placed in a certain order?
    Does not have to be order used in the query
    All three functions return a number
    Difference between functions is how they handle ties
    These function are analytic only.

@@@
@@@<1> basic rank function
@@@
SCOTT@ocm> !cat tmp.sql
set echo off
set feedback off
set verify off
set autot off
prompt @@@RANK() DENSE_RANK() ROW_NUMBER()
prompt ~  display current record ranking of order by ename.
prompt ~  When there are no ties, all three functions return
prompt ~  the same values.
SELECT  deptno, ename, sal
      , RANK()       OVER(ORDER BY ename) f1
      , DENSE_RANK() OVER(ORDER BY ename) f2
      , ROW_NUMBER() OVER(ORDER BY ename) f3
  FROM  scott.emp
 ORDER BY deptno, sal
/
prompt
prompt Observe the output, comparing with above
SELECT  ename
  FROM scott.emp
ORDER BY ename
/

SCOTT@ocm> @tmp.sql
@@@RANK() DENSE_RANK() ROW_NUMBER()
~  display current record ranking of order by ename.
~  When there are no ties, all three functions return
~  the same values.

    DEPTNO ENAME         SAL     F1        F2           F3
---------- ---------- ---------- ---------- ---------- ----------
    10 MILLER        1300     10        10           10
    10 CLARK        2450      4         4        4
    10 KING         5000      8         8        8
    20 SMITH         800     12        12           12
    20 ADAMS        1100      1         1        1
    20 JONES        2975      7         7        7
    20 FORD         3000      5         5        5
    20 SCOTT        3000     11        11           11
    30 JAMES         950      6         6        6
    30 WARD         1250     14        14           14
    30 MARTIN        1250      9         9        9
    30 TURNER        1500     13        13           13
    30 ALLEN        1600      2         2        2
    30 BLAKE        2850      3         3        3

Observe the output, comparing with above

ENAME
----------
ADAMS
ALLEN
BLAKE
CLARK
FORD
JAMES
JONES
KING
MARTIN
MILLER
SCOTT
SMITH
TURNER
WARD


@@@
@@@<2> ranking functions with ties
@@@
SCOTT@ocm> !cat tmp.sql
set echo off
set feedback off
set verify off
set autot off
prompt @@@RANK() DENSE_RANK() ROW_NUMBER()
prompt ~  display current record ranking of order by sal.
prompt ~  However, if you have the same salary, different function would
prompt ~  handle by different method.
prompt ~  If repeat 3 4 5 6 :
prompt ~     RANK()       ===> 3 3 3 6  (repect the order)
prompt ~     DENSE_RANK() ===> 3 3 3 4  (densty)
prompt ~     ROW_NUMBER() ===> 3 4 5 6  (ranking by rownum)
SELECT  deptno, ename, sal
      , RANK()       OVER(ORDER BY sal) f1
      , DENSE_RANK() OVER(ORDER BY sal) f2
      , ROW_NUMBER() OVER(ORDER BY sal) f3
  FROM  scott.emp
 ORDER BY deptno, sal
/
prompt
prompt Observe the output, comparing with above
SELECT  sal
  FROM scott.emp
ORDER BY sal
/

SCOTT@ocm> @tmp.sql
@@@RANK() DENSE_RANK() ROW_NUMBER()
~  display current record ranking of order by sal.
~  However, if you have the same salary, different function would
~  handle by different method.
~  If repeat 3 4 5 6 :
~     RANK()       ===> 3 3 3 6  (repect the order)
~     DENSE_RANK() ===> 3 3 3 4  (densty)
~     ROW_NUMBER() ===> 3 4 5 6  (ranking by rownum)

    DEPTNO ENAME         SAL     F1        F2           F3
---------- ---------- ---------- ---------- ---------- ----------
    10 MILLER        1300      6         5        6
    10 CLARK        2450      9         8        9
    10 KING         5000     14        12           14
    20 SMITH         800      1         1        1
    20 ADAMS        1100      3         3        3
    20 JONES        2975     11        10           11
    20 FORD         3000     12        11           13
    20 SCOTT        3000     12        11           12
    30 JAMES         950      2         2        2
    30 WARD         1250      4         4        4
    30 MARTIN        1250      4         4        5
    30 TURNER        1500      7         6        7
    30 ALLEN        1600      8         7        8
    30 BLAKE        2850     10         9           10

Observe the output, comparing with above

       SAL
----------
       800
       950
      1100
      1250
      1250
      1300
      1500
      1600
      2450
      2850
      2975
      3000
      3000
      5000

@@@
@@@<3> ROW_NUMBER() only display the row number
@@@
SCOTT@ocm> !cat tmp.sql
set echo off
set feedback off
set verify off
set autot off
prompt @@@RANK() DENSE_RANK() ROW_NUMBER()
prompt ~  display current record ranking of order by sal.
prompt ~  However, if you have the same salary, different function would
prompt ~  handle by different method.
prompt ~  If repeat 3 4 5 6 :
prompt ~     RANK()       ===> 3 3 3 6  (repect the order)
prompt ~     DENSE_RANK() ===> 3 3 3 4  (densty)
prompt ~     ROW_NUMBER() ===> 3 4 5 6  (ranking by rownum)
SELECT  deptno, ename, sal
      , ROW_NUMBER() OVER(ORDER BY sal DESC)             f1
      , ROW_NUMBER() OVER(PARTITION BY job ORDER BY sal) f2
  FROM  scott.emp
 ORDER BY deptno, sal
/
prompt
prompt Observe the output, comparing with above
SELECT  sal
  FROM scott.emp
ORDER BY sal
/

SCOTT@ocm> @tmp.sql
@@@RANK() DENSE_RANK() ROW_NUMBER()
~  display current record ranking of order by sal.
~  However, if you have the same salary, different function would
~  handle by different method.
~  If repeat 3 4 5 6 :
~     RANK()       ===> 3 3 3 6  (repect the order)
~     DENSE_RANK() ===> 3 3 3 4  (densty)
~     ROW_NUMBER() ===> 3 4 5 6  (ranking by rownum)

    DEPTNO ENAME         SAL     F1        F2
---------- ---------- ---------- ---------- ----------
    10 MILLER        1300      9         4
    10 CLARK        2450      6         1
    10 KING         5000      1         1
    20 SMITH         800     14         1
    20 ADAMS        1100     12         3
    20 JONES        2975      4         3
    20 FORD         3000      2         1
    20 SCOTT        3000      3         2
    30 JAMES         950     13         2
    30 MARTIN        1250     10         1
    30 WARD         1250     11         2
    30 TURNER        1500      8         3
    30 ALLEN        1600      7         4
    30 BLAKE        2850      5         2

Observe the output, comparing with above

       SAL
----------
       800
       950
      1100
      1250
      1250
      1300
      1500
      1600
      2450
      2850
      2975
      3000
      3000
      5000


@@@
@@@<4> Using ORDER BY change windows
@@@
SCOTT@ocm> !cat tmp.sql
set echo off
set feedback off
set verify off
set autot off
prompt @@@SUM(),COUNT(),MAX(),MIN() with OVER()
prompt ~  use order by to change window
prompt ~  current record inculding upward records as the object of function
prompt ~  , when use order by only.
SELECT  deptno, ename, sal
      , SUM(sal) OVER(ORDER BY ename) sal
      , COUNT(*) OVER(ORDER BY ename) cnt
      , MIN(sal) OVER(ORDER BY ename) min
      , MAX(sal) OVER(ORDER BY ename) max
  FROM scott.emp
 WHERE deptno=10
/
SELECT deptno, ename, sal
  FROM scott.emp
 WHERE deptno=10
/

SCOTT@ocm> @tmp.sql
@@@SUM(),COUNT(),MAX(),MIN() with OVER()
~  use order by to change window
~  current record inculding upward records as the object of function
~  , when use order by only.

    DEPTNO ENAME         SAL    SAL       CNT          MIN     MAX
---------- ---------- ---------- ---------- ---------- ---------- ----------
    10 CLARK        2450       2450         1         2450    2450
    10 KING         5000       7450         2         2450    5000
    10 MILLER        1300       8750         3         1300    5000

    DEPTNO ENAME         SAL
---------- ---------- ----------
    10 CLARK        2450
    10 KING         5000
    10 MILLER        1300

@@@Why?
  This is the default behavior.
  If you include an ORDER BY where one would not be necessary, Oracle assume it is
  there for a reason.
     1+3+5=9 and 3+5+1=9 the same.
  Very powerful for running calculations, such as MTD:
     SUM(sales) OVER(ORDER BY week_number) AS Month To Date
  result like:
     Week Number    Sales      Month To Date
     -----------    -------    --------------
     1              11,000     11,000
     2              15,000     26,000
     3              12,000     38,000
     4              16,000     54,000



@@@
@@@<5> Scope of Windowing, ROWS and RANGE
@@@
Example01, ROWS:

  Demonstration of default windowing, with and without ORDER BY
   ^
   |   ^
  ---  |    ^
  --------  |
  ------------ meaning between unbounded preceding and current row
 
   ^   ^   ^ 
   |   |   |
   |   |   |
   |   |   |   meaning between unbounded preceding and unbounded following
  
SCOTT@ocm> !cat tmp.sql
set echo off
set feedback off
set verify off
set autot off
prompt @@@Control Scope of Windowsing
prompt ~ unbounded preceding and unbounded following == all area
prompt ~ unbounded preceding and current row         == order by xxx
SELECT  deptno, ename, sal
      , SUM(sal) OVER() sum1
      , SUM(sal) OVER(ORDER BY ename) sum2
      , SUM(sal) OVER(ORDER BY ename
        ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) sum3
      , SUM(sal) OVER(ORDER BY ename
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) sum4
  FROM scott.emp
 WHERE deptno=10
/

SCOTT@ocm> @tmp.sql
@@@Control Scope of Windowsing
~ unbounded preceding and unbounded following == all area
~ unbounded preceding and current row          == order by xxx

    DEPTNO ENAME         SAL       SUM1      SUM2         SUM3    SUM4
---------- ---------- ---------- ---------- ---------- ---------- ----------
    10 CLARK        2450       8750      2450         8750    2450
    10 KING         5000       8750      7450         8750    7450
    10 MILLER        1300       8750      8750         8750    8750


Example02, ROWS:
SCOTT@ocm> !cat tmp.sql
set echo off
set feedback off
set verify off
set autot off
prompt @@@Control Scope of Windowing
prompt ~  the same up one, down one and current one, plus threes
prompt ~  equal to current sum value. 
SELECT  deptno, ename, sal
      , SUM(sal) OVER(ORDER BY ename
        ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) sum1
  FROM scott.emp
/
SELECT  deptno, ename, sal
      , SUM(sal) OVER(PARTITION BY deptno ORDER BY ename
        ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) sum2
  FROM scott.emp
ORDER BY deptno
/

SCOTT@ocm> @tmp.sql
@@@Control Scope of Windowing
~  the same up one, down one and current one, plus threes
~  equal to current sum value.

    DEPTNO ENAME         SAL       SUM1
---------- ---------- ---------- ----------
    20 ADAMS        1100       2700  -- -----0+1100+1600=2700
    30 ALLEN        1600       5550  --
    30 BLAKE        2850       6900
    10 CLARK        2450       8300
    20 FORD         3000       6400
    30 JAMES         950       6925  --
    20 JONES        2975       8925  -- -----950+2975+5000=8925
    10 KING         5000       9225  --
    30 MARTIN        1250       7550 
    10 MILLER        1300       5550
    20 SCOTT        3000       5100
    20 SMITH         800       5300
    30 TURNER        1500       3550  --
    30 WARD         1250       2750  -- -----1500+1250+0=2750

    DEPTNO ENAME         SAL       SUM2
---------- ---------- ---------- ----------
    10 CLARK        2450       7450
    10 KING         5000       8750
    10 MILLER        1300       6300 -------
    20 ADAMS        1100       4100
    20 FORD         3000       7075
    20 JONES        2975       8975
    20 SCOTT        3000       6775
    20 SMITH         800       3800 -------
    30 ALLEN        1600       4450
    30 BLAKE        2850       5400
    30 JAMES         950       5050
    30 MARTIN        1250       3700
    30 TURNER        1500       4000
    30 WARD         1250       2750 ------

Example03, RANGE:
like:
  RANGE BETWEEN INTERVAL '10' DAY PRECEDING
            AND INTERVAL '10' DAY FOLLOWING

SCOTT@ocm> !cat tmp.sql
set echo off
set feedback off
set verify off
set autot off
prompt @@@Control Scope of Windowing
prompt ~  Range mean the value of operational field, could be date
prompt ~  number.
SELECT  ename, sal
      , COUNT(*) OVER(ORDER BY sal
        RANGE BETWEEN 200 PRECEDING AND 200 FOLLOWING) emps_200_sal
  FROM scott.emp
/
ALTER SESSION SET NLS_DATE_FORMAT='yyyy-mm-dd'
/
SELECT  ename, hiredate
      , COUNT(*) OVER(ORDER BY hiredate
        RANGE BETWEEN INTERVAL '1' YEAR + INTERVAL '2' MONTH PRECEDING
                  AND INTERVAL '1' YEAR + INTERVAL '2' MONTH FOLLOWING)
        emp_year_month
      , COUNT(*) OVER(ORDER BY hiredate
        RANGE BETWEEN INTERVAL '3' MONTH PRECEDING
                  AND INTERVAL '3' MONTH FOLLOWING)
        emp_day
  FROM scott.emp
/

SCOTT@ocm> @tmp.sql
@@@Control Scope of Windowing
~  Range mean the value of operational field, could be date
~  number.

ENAME          SAL EMPS_200_SAL
---------- ---------- ------------
SMITH          800         2
JAMES          950         3
ADAMS         1100         5
WARD         1250         4
MARTIN         1250         4
MILLER         1300         5
TURNER         1500         3
ALLEN         1600         2
CLARK         2450         1
BLAKE         2850         4
JONES         2975         4
SCOTT         3000         4
FORD         3000         4
KING         5000         1

ENAME       HIREDATE   EMP_YEAR_MONTH    EMP_DAY
---------- ---------- -------------- ----------
SMITH       1980-12-17          12          3
ALLEN       1981-02-20          12          5
WARD       1981-02-22          12          5
JONES       1981-04-02          12          5
BLAKE       1981-05-01          12          5
CLARK       1981-06-09          12          4
TURNER       1981-09-08          12          6
MARTIN       1981-09-28          12          5
KING       1981-11-17          12          6
JAMES       1981-12-03          12          6
FORD       1981-12-03          12          6
MILLER       1982-01-23          12          4
SCOTT       1987-04-19           2          2
ADAMS       1987-05-23           2          2




Example04, RANGE:
like
   INTERVAL '7' HOUR
   INTERVAL '7:45' HOUR TO MINUTE
   INTERVAL '7:45' MINUTE TO SECOND
   INTERVAL '7:45:00' HOUR TO SECOND
   INTERVAL '3 7:45:00' DAY TO SECOND
   INTERVAL '3 7:45' DAY TO MINUTE
 
SCOTT@ocm> !cat tmp.sql
set echo off
set feedback off
set verify off
set autot off
set lines 120
prompt @@@Control Scope of Windowing
prompt ~  Range mean the value of operational field, could be date
prompt ~  number.
prompt ~  Here is two following, the same meaing way.
ALTER SESSION SET NLS_DATE_FORMAT='yyyy-mm-dd'
/
SELECT  empno, ename, hiredate
      , COUNT(*) OVER(ORDER BY hiredate
        RANGE BETWEEN INTERVAL '1 3' DAY TO HOUR FOLLOWING
                  AND INTERVAL '1-6' YEAR TO MONTH FOLLOWING)
        example
  FROM scott.emp
/
col interv_1 for a20
col interv_2 for a20
col interv_3 for a20
col interv_4 for a20
SELECT  INTERVAL '3' DAY                     AS interv_1
      , INTERVAL '3 00:00:00' DAY TO SECOND  AS interv_2  
      , INTERVAL '72' HOUR                   AS interv_3
      , INTERVAL '4320' MINUTE               AS interv_4
  FROM dual
/
SELECT  INTERVAL '7' SECOND                   AS interv_1
      , INTERVAL '09:09:09' HOUR TO SECOND    AS interv_2  
      , INTERVAL '06:09' HOUR TO MINUTE       AS interv_3
      , INTERVAL '04:20' MINUTE TO SECOND     AS interv_4
  FROM dual
/


SCOTT@ocm> @tmp.sql
@@@Control Scope of Windowing
~  Range mean the value of operational field, could be date
~  number.
~  Here is two following, the same meaing way.

     EMPNO ENAME      HIREDATE        EXAMPLE
---------- ---------- ---------- ----------
      7369 SMITH      1980-12-17     11
      7499 ALLEN      1981-02-20     10
      7521 WARD       1981-02-22      9
      7566 JONES      1981-04-02      8
      7698 BLAKE      1981-05-01      7
      7782 CLARK      1981-06-09      6
      7844 TURNER     1981-09-08      5
      7654 MARTIN     1981-09-28      4
      7839 KING       1981-11-17      3
      7900 JAMES      1981-12-03      1
      7902 FORD       1981-12-03      1
      7934 MILLER     1982-01-23      0
      7788 SCOTT      1987-04-19      1
      7876 ADAMS      1987-05-23      0

INTERV_1         INTERV_2          INTERV_3           INTERV_4
-------------------- -------------------- -------------------- --------------------
+03 00:00:00         +03 00:00:00.000000  +03 00:00:00           +03 00:00:00

INTERV_1         INTERV_2          INTERV_3           INTERV_4
-------------------- -------------------- -------------------- --------------------
+00 00:00:07.000000  +00 09:09:09.000000  +00 06:09:00           +00 00:04:20.000000



Example05, RANGE:
SCOTT@ocm> !cat tmp.sql
set echo off
set feedback off
set verify off
set autot off
set lines 120
prompt @@@Control Scope of Windowing
prompt ~  Range mean the value of operational field, could be date
prompt ~  number.
prompt ~  Here is two following, the same meaing way.
ALTER SESSION SET NLS_DATE_FORMAT='yyyy-mm-dd'
/
SELECT  empno, ename, hiredate
      , COUNT(*) OVER(ORDER BY hiredate
        RANGE BETWEEN INTERVAL '2' MONTH FOLLOWING
                  AND INTERVAL '6' MONTH FOLLOWING)
        emp_after_2_6
      , COUNT(*) OVER(ORDER BY hiredate
        RANGE BETWEEN CURRENT ROW
                  AND INTERVAL '6' MONTH FOLLOWING)
        emp_after_cur_6
  FROM scott.emp
/

SCOTT@ocm> @tmp.sql
@@@Control Scope of Windowing
~  Range mean the value of operational field, could be date
~  number.
~  Here is two following, the same meaing way.

     EMPNO ENAME      HIREDATE     EMP_AFTER_2_6 EMP_AFTER_CUR_6
---------- ---------- ---------- ------------- ---------------
      7369 SMITH      1980-12-17         5             6
      7499 ALLEN      1981-02-20         2             5
      7521 WARD       1981-02-22         2             4
      7566 JONES      1981-04-02         3             5
      7698 BLAKE      1981-05-01         2             4
      7782 CLARK      1981-06-09         5             6
      7844 TURNER     1981-09-08         4             6
      7654 MARTIN     1981-09-28         3             5
      7839 KING       1981-11-17         1             4
      7900 JAMES      1981-12-03         0             3
      7902 FORD       1981-12-03         0             3
      7934 MILLER     1982-01-23         0             1
      7788 SCOTT      1987-04-19         0             2
      7876 ADAMS      1987-05-23         0             1



5@@@@Conprehensive Example
@@@
@@@<1> Example01 , partition by order by
@@@
OE@ocm> !cat tmp.sql
set echo off
set feedback off
set verify off
set autot off
set lines 120
prompt @@@Application Example
ALTER SESSION SET NLS_DATE_FORMAT='yyyy-mm-dd'
/
SELECT  customer_id
      , TRUNC(order_date) AS order_date
      , order_total
      , LEAD( TRUNC(order_date) )
        OVER( PARTITION BY customer_id ORDER BY order_date)
        AS next_order_date
  FROM oe.orders
 WHERE customer_id IN ( 103, 105 )
 ORDER BY 1,2
/

OE@ocm> @tmp.sql
@@@Application Example

CUSTOMER_ID ORDER_DATE ORDER_TOTAL NEXT_ORDER
----------- ---------- ----------- ----------
    103 1997-03-30           310 1998-09-01
    103 1998-09-01         13550 1999-09-14
    103 1999-09-14        78 1999-10-03
    103 1999-10-03        6653.4
    105 1999-03-21        1926.6 1999-09-01
    105 1999-09-01       22150.1 2000-01-09
    105 2000-01-09          7826 2000-01-27
    105 2000-01-27       29473.8



@@@
@@@<2> Example02, compare this_plus_next and sum_this_next
@@@
OE@ocm> !cat tmp.sql
set echo off
set feedback off
set verify off
set autot off
set lines 120
prompt @@@Application Example
prompt ~  NULL + order_total = NULL ?
ALTER SESSION SET NLS_DATE_FORMAT='yyyy-mm-dd'
/
SELECT  customer_id
      , TRUNC(order_date) AS order_date
      , order_total
      , LEAD( TRUNC(order_date) ) OVER
        (PARTITION BY customer_id ORDER BY order_date) AS next_order
      , order_total + LEAD(order_total) OVER
        (PARTITION BY customer_id ORDER BY order_date) AS this_plus_next
      , SUM(order_total) OVER
        (PARTITION BY customer_id ORDER BY order_date
         ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) AS sum_this_next
  FROM oe.orders
 WHERE customer_id IN ( 103, 105 )
 ORDER BY 1,2
/

OE@ocm> @tmp.sql
@@@Application Example
~  NULL + order_total = NULL ?

CUSTOMER_ID ORDER_DATE ORDER_TOTAL NEXT_ORDER THIS_PLUS_NEXT SUM_THIS_NEXT
----------- ---------- ----------- ---------- -------------- -------------
    103 1997-03-30           310 1998-09-01           13860         13860
    103 1998-09-01         13550 1999-09-14           13628         13628
    103 1999-09-14        78 1999-10-03          6731.4        6731.4
    103 1999-10-03        6653.4                    6653.4
    105 1999-03-21        1926.6 1999-09-01         24076.7       24076.7
    105 1999-09-01       22150.1 2000-01-09         29976.1       29976.1
    105 2000-01-09          7826 2000-01-27         37299.8       37299.8
    105 2000-01-27       29473.8                   29473.8

@@@
@@@<3> Shutcut for Scope of Windowing
@@@
ROWS UNBOUNDED PRECEDING == ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
ROWS 10 PRECEDING == ROWS BETWEEN 10 PRECEDING AND CURRENT ROW
ROWS CURRENT ROW  == ROWS BETWEEN CURRENT ROW AND CURRENT ROW