(1) 函數: over()的作用及用法: -- 分區(分組)求和。
sum() over( partition by column1 order by column2 )主要用來對某個字段值進行逐步累加,連續求和;
排名函數:
RANK ( ) OVER ( [query_partition_clause] order_by_clause ) --
DENSE_RANK ( ) OVER ( [query_partition_clause] order_by_clause )
可實現按指定的字段分組排序,對於相同分組字段的結果集進行排序,
其中PARTITION BY 為分組字段,ORDER BY 指定排序字段;
區別:
RANK() 有排名並列名次的時候會產生不連續的排名編號,例如數據值 1,2,2,3 排名后發生的編號將是1,2,2,4;
DENSE_RANK() 有並列名次的時候會產生持續的排名編號,例如數據值 1,2,2,3 排名后發生的編號將是1,2,2,3;
over不能單獨使用,要和分析函數:rank(),dense_rank(),row_number()/sum() 等一起使用。
其參數:over(partition by columnname1 order by columnname2)
含義:按columname1指定的字段進行分組排序,或者說按字段columnname1的值進行分組排序。
1.例如:employees表中,有兩個部門的記錄:department_id =10和20
select department_id,rank() over(partition by department_id order by salary) from employees就是指在部門10中進行薪水的排名,在部門20中進行薪水排名。如果是partition by org_id,則是在整個公司內進行排名。(org_id: 公司ID)
2.使用子分區(分組)查出各部門薪水連續的總和。注意按部門分區(分組)。注意over(...)條件的不同,
sum(sal) over (partition by deptno order by ename) 按部門“連續”求總和
sum(sal) over (partition by deptno) 按部門求總和
sum(sal) over (order by deptno,ename) 不按部門“連續”求總和
sum(sal) over () 不按部門,求所有員工總和,效果等同於sum(sal)。
SQL> break on deptno skip 1 -- 為效果更明顯,把不同部門的數據隔段顯示。
SQL> select deptno,ename,sal,
2 sum(sal) over (partition by deptno order by ename) 部門連續求和,--各部門的薪水"連續"求和
3 sum(sal) over (partition by deptno) 部門總和, -- 部門統計的總和,同一部門總和不變
4 sum(sal) over (order by deptno,ename) 連續求和, --所有部門的薪水"連續"求和
5 sum(sal) over () 總和, -- 此處sum(sal) over () 等同於sum(sal),所有員工的薪水總和
6 from emp ;
Result :

(2) Oracle中使用函數rank()over()排名的問題 -- 不連續/連續排名
排序:
RANK( ) OVER(order by 列名 排序) 的結果是不連續的,如果有4個人,其中有3個是並列第1名,那么最后的排序結果結果如:1 1 1 4;
DENSE_RANK( ) OVER(order by 列名 排序) 的結果是連續的,如果有4個人,其中有3個是並列第1名, 那么最后的排序結果如:1 1 1 2;
如:
1. SELECT COLA , RANK( ) OVER(order by COLE ASC ) E FROM TABLE_A ;
2. SELECT COLA , RANK( ) OVER(order by COLE ASC ) E FROM TABLE_A ;
Result :

(3)--用row_number() over(partition by column_name1,column_name2 order by column_name3) 去除重復以partition by 所列的column的重復記錄
select *
from(
select
ID_clo,
key_clo,
clo1,clo2 ,
date_col,
row_number() over(partition by ID_clo,key_clo order by date_col desc) as desc_rk
from my_table_name ) t
where t.desc_rk =1
常用函數:
1. Round():[舍入到最接近的日期],無時分秒; (day:舍入到最接近的星期日);
select sysdate S1, round(sysdate) S2 , round(sysdate,'year') YEAR, round(sysdate,'month') MONTH , round(sysdate,'day') DAY from dual;


2. TRUNC(): (1)[截斷到最接近的日期,單位為天] ,返回的是日期類型,無時分秒;如:trunc(sysdate,'month')--返回當前月的1日(月初);
(2) 按照指定的精度截取一個數;如:SELECT TRUNC(11124.1888,-2) TRUNC1,TRUNC(111124.1888,2) FROM DUAL;
select sysdate S1, trunc(sysdate) S2, --返回當前日期,無時分秒 trunc(sysdate,'year') YEAR, --返回當前年的1月1日,無時分秒(年初) trunc(sysdate,'month') MONTH , --返回當前月的1日,無時分秒 (月初) trunc(sysdate,'day') DAY --返回當前星期的星期天,無時分秒 (星期初,即星期的第一天) from dual;

SELECT TRUNC(11124.1888,-2) TRUNC1,
TRUNC(111124.1888,2)
FROM DUAL;

3. LAST_DAY(): 返回日期的最后一天,有時分秒;
SELECT SYSDATE,
LAST_DAY(SYSDATE) LAST_DATE
FROM DUAL; -- 返回日期的最后一天

SELECT SYSDATE,
TRUNC(TRUNC(SYSDATE, 'MONTH') - 1, 'MONTH') FIRST_DAY_LAST_MONTH,
TRUNC(SYSDATE, 'MONTH') - 1 / 86400 LAST_DAY_LAST_MONTH,
TRUNC(SYSDATE, 'MONTH') FIRST_DAY_CUR_MONTH,
LAST_DAY(TRUNC(SYSDATE, 'MONTH')) + 1 - 1 / 86400 LAST_DAY_CUR_MONTH,
LAST_DAY(TRUNC(SYSDATE, 'MONTH')) + 1 FIRST_DAY_NEXT_MONTH
FROM DUAL;

4. ADD_MONTHS(): 增加或減去月份
SELECT SYSDATE,
ADD_MONTHS(TRUNC(SYSDATE, 'MONTH'), 2),
ADD_MONTHS(TRUNC(SYSDATE, 'MONTH'), -13)
FROM DUAL;

5. MONTHS_BETWEEN(date1,date2) : 給出date1-date2的月份(date1減去date2);
SELECT SYSDATE,
MONTHS_BETWEEN(SYSDATE,TO_DATE('20141220', 'YYYY-MM-DD')) MON_BETW1,
MONTHS_BETWEEN(TO_DATE('20141220', 'YYYY-MM-DD'), SYSDATE) MON_BETW2,
TRUNC(MONTHS_BETWEEN(TO_DATE('20141220', 'YYYY-MM-DD'), SYSDATE)) MON_BETW3
FROM DUAL;

6. NEXT_DAY(date,day):給出日期date和星期x之后計算接下來的最近一個星期x的日期。day為1-7或星期日-星期六,1表示星期日。
next_day(sysdate,6)是從當前開始下一個星期五。后面的數字是從星期日開始算起。
SELECT SYSDATE, --當前日期是周四
NEXT_DAY(SYSDATE, 2) NEXT_DAY1, --周一對應的日期
NEXT_DAY(SYSDATE, 7) NEXT_DAY2 --周六對應的日期
FROM DUAL;

7. extract() : 截取出日期或間隔值的字段值;
SELECT SYSDATE,
EXTRACT(HOUR FROM TIMESTAMP '2017-02-16 11:38:40') "HOUR" ,
EXTRACT(DAY FROM SYSDATE) "DAY",
EXTRACT(MONTH FROM SYSDATE) "MONTH",
EXTRACT(YEAR FROM SYSDATE) "YEAR"
FROM DUAL ;
SELECT SYSDATE,
TO_CHAR(SYSDATE, 'HH'),
TO_CHAR(SYSDATE, 'HH24')
FROM DUAL;

8. CONCAT(‘a’,'b'): 連接a、b兩個字符串;
SELECT CONCAT(CONCAT('張', '--'), '小姐') AS A,
'張' || '--' || '小姐' AS B
FROM DUAL;

9. INITCAP():返回字符串並將字符串中每一個單詞的首字母變為大寫,其他字符小寫。單詞有空格或非字母的字符隔開;
SELECT INITCAP('LAKALA') AS A,
INITCAP('lakala') AS B,
INITCAP('la1ka?la') AS C,
INITCAP('la ka la') AS D,
INITCAP('la ka3la') AS E
FROM DUAL;

(轉載):
2.1.2 INSTR(C1,C2,I,J)
在一個字符串中搜索指定的字符,返回發現指定的字符的位置;
C1 被搜索的字符串
C2 希望搜索的字符串
I 搜索的開始位置,默認為1
J 出現的位置,默認為1
HR@test>select instr('lakala LAKALA','la',1,1),instr('lakala LAKALA','la',1,2) instring from dual;
INSTR('LAKALALAKALA','LA',1,1) INSTRING
------------------------------ ----------
1 5
2.1.3 LENGTH
返回字符串的長度;
SQL> select length(null),length(''),length(' '),length('lakala') from dual;
LENGTH(NULL) LENGTH('') LENGTH('') LENGTH('LAKALA')
------------ ---------- ---------- ----------------
1 6
2.1.4 LOWER
返回字符串,並將所有的字符小寫
SYS@test> select lower('LaKaLa') from dual;
LOWER('LAKAL
------------
lakala
2.1.5 UPPER
返回字符串,並將所有的字符大寫
SYS@test> select upper('LaKaLa') from dual;
UPPER('LAKAL
------------
LAKALA
2.1.6 RPAD和LPAD
(填充字符)RPAD 在列的右邊填充字符/LPAD 在列的左邊填充字符
SYS@test>select rpad('lakala',10,'#'), lpad('222.111',10,'0')from dual;
RPAD('LAKALA',10,'#' LPAD('222.111',10,'0
-------------------- --------------------
lakala#### 000222.111
2.1.7 LTRIM和RTRIM/ TRIM(s from string)
LTRIM 刪除左邊出現的字符串,RTRIM 刪除右邊出現的字符串.
LEADING 剪掉前面的字符
TRAILING 剪掉后面的字符
如果不指定,默認為空格符
SYS@test>select rtrim('#la#ka#la###','#'),ltrim('###la#ka#la#','#'),trim( '#' from '###la#ka#la###') from dual;
RTRIM('#LA#KA#LA## LTRIM('###LA#KA#LA TRIM('#'FROM'###
------------------ ------------------ ----------------
#la#ka#la la#ka#la# la#ka#la
2.1.8 SUBSTR(string,start,count)
取子字符串,從start開始,取count個
SYS@test>select substr('010-56710999',1,4),substr('010-56710999',5,8) from dual;
SUBSTR(' SUBSTR('010-5671
-------- ----------------
010- 56710999
2.1.9 REPLACE(string,s1,s2)
string 希望被替換的字符或變量
s1 被替換的字符串
s2 要替換的字符串
SYS@test>select replace('la#ka#la','#','') from dual;
REPLACE('LA#
------------
lakala
2.2 數字函數
3 ABS
返回指定值的絕對值
SYS@test>select abs(100),abs(-100) from dual;
ABS(100) ABS(-100)
---------- ----------
100 100
3.1.1 FLOOR
對給定的數字取整數
SYS@test>select floor(11.92),floor(-11.02) from dual;
FLOOR(11.92) FLOOR(-11.02)
------------ -------------
11 -12
3.1.2 MOD(n1,n2)
返回一個n1除以n2的余數
SYS@test> select mod(12,3),mod(1,3),mod(2,3) from dual;
MOD(12,3) MOD(1,3) MOD(2,3)
---------- ---------- ----------
0 1 2
3.1.3 ROUND和TRUNC
按照指定的精度進行舍入
SYS@test> select round(11.9),round(-11.4),trunc(11.9),trunc(-11.1) from dual;
ROUND(11.9) ROUND(-11.4) TRUNC(11.9) TRUNC(-11.1)
----------- ------------ ----------- ------------
12 -11 11 -11
例子:

3.1.4 SIGN
取數字n的符號,大於0返回1,小於0返回-1,等於0返回0
SYS@test>select sign(12),sign(-12),sign(0) from dual;
SIGN(12) SIGN(-12) SIGN(0)
---------- ---------- ----------
1 -1 0
3.1.5 TRUNC
按照指定的精度截取一個數
SYS@test>select trunc(11124.1888,-2) trunc1,trunc(111124.1888,2) from dual;
TRUNC1 TRUNC(111124.1888,2)
---------- --------------------
11100 111124.18
3.2 轉換函數
TO_DATE格式(以時間:2007-11-02 13:45:25為例)
Year:
yy two digits 兩位年 顯示值:07
yyy three digits 三位年 顯示值:007
yyyy four digits 四位年 顯示值:2007
Month:
mm number 兩位月 顯示值:11
mon abbreviated 字符集表示 顯示值:11月,若是英文版,顯示nov
month spelled out 字符集表示 顯示值:11月,若是英文版,顯示november
Day:
dd number 當月第幾天 顯示值:02
ddd number 當年第幾天 顯示值:02
dy abbreviated 當周第幾天簡寫 顯示值:星期五,若是英文版,顯示fri
day spelled out 當周第幾天全寫 顯示值:星期五,若是英文版,顯示friday
D Day of week (1-7). This element depends on the NLS territory of the session.
Hour:
hh two digits 12小時進制 顯示值:01
hh24 two digits 24小時進制 顯示值:13
Minute:
mi two digits 60進制 顯示值:45
Second:
ss two digits 60進制 顯示值:25
其它
Q digit 季度 顯示值:4
WW digit 當年第幾周 顯示值:44
W digit 當月第幾周 顯示值:1
24小時格式下時間范圍為: 0:00:00 - 23:59:59....
12小時格式下時間范圍為: 1:00:00 - 12:59:59 ....
4 TO_CHAR(date,format)
SYS@test>select to_char(sysdate,'WW W Q ddd D'),sysdate from dual;
TO_CHAR(SYSDATE,'WWWQDDD SYSDATE
------------------------ -----------------------
11 3 1 076 2 17-MAR-2014 16:53:41
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') as nowTime from dual; //日期轉化為字符串
select to_char(sysdate,'yyyy') as nowYear from dual; //獲取時間的年
select to_char(sysdate,'mm') as nowMonth from dual; //獲取時間的月
select to_char(sysdate,'dd') as nowDay from dual; //獲取時間的日
select to_char(sysdate,'hh24') as nowHour from dual; //獲取時間的時
select to_char(sysdate,'mi') as nowMinute from dual; //獲取時間的分
select to_char(sysdate,'ss') as nowSecond from dual; //獲取時間的秒
4.1.1 TO_DATE(string,format)
將字符串轉化為ORACLE中的一個日期
4.1.2 TO_NUMBER
將給出的字符轉換為數字
SYS@test>select to_number('2014') year from dual;
YEAR
----------
2014
4.2 Null函數
NVL(expr1, expr2)->expr1為NULL,返回expr2;不為NULL,返回expr1。注意兩者的類型要一致.
NVL2 (expr1, expr2, expr3) ->expr1不為NULL,返回expr2;為NULL,返回expr3。expr2和expr3類型不同的話,expr3會轉換為expr2的類型 .
NULLIF (expr1, expr2) ->相等返回NULL,不等返回expr1.
5 nvl(ex1,ex2):
ex1值為空則返回ex2,否則返回該值本身ex1(常用)
例:如果雇員沒有佣金,將顯示0,否則顯示佣金
SCOTT@test>select comm,nvl(comm,0) from emp;
COMM NVL(COMM,0)
---------- -----------
0
300 300
500 500
0
1400 1400
0
0
0
0
0 0
0
0
0
0
5.1.1 nvl2(ex1,ex2,ex3) :
如果ex1不為空,顯示ex2,否則顯示ex3
HR@test>SELECT last_name, salary,
2 NVL2(commission_pct, salary + (salary * commission_pct), salary) income
3 FROM hr.employees
4 WHERE last_name like 'B%'
5 ORDER BY last_name;
LAST_NAME SALARY INCOME
-------------------------------------------------- ---------- ----------
Baer 10000 10000
Baida 2900 2900
Banda 6200 6820
Bates 7300 8395
Bell 4000 4000
Bernstein 9500 11875
Bissot 3300 3300
Bloom 10000 12000
Bull 4100 4100
5.1.2 nullif(ex1,ex2):
值相等返空,否則返回第一個值
HR@test>SELECT e.last_name, NULLIF(e.job_id, j.job_id) "Old Job ID"
2 FROM employees e, job_history j
3 WHERE e.employee_id = j.employee_id
4 ORDER BY last_name, "Old Job ID";
LAST_NAME Old Job ID
-------------------------------------------------- --------------------
De Haan AD_VP
Hartstein MK_MAN
Kaufling ST_MAN
Kochhar AD_VP
Kochhar AD_VP
Raphaely PU_MAN
Taylor SA_REP
Taylor
Whalen AD_ASST
Whalen
5.1.3 coalesce:
返回列表中第一個非空表達式
OE@test>SELECT product_id, list_price, min_price,
2 COALESCE(0.9*list_price, min_price, 5) "Sale"
3 FROM product_information
4 WHERE supplier_id = 102050
5 ORDER BY product_id;
PRODUCT_ID LIST_PRICE MIN_PRICE Sale
---------- ---------- ---------- ----------
1769 48 43.2
1770 73 73
2378 305 247 274.5
2382 850 731 765
3355 5
5.2 其他函數
6 Decode
[實現if ..then 邏輯] 注:第一個是表達式,最后一個是不滿足任何一個條件的值
SCOTT@test>select deptno,decode(deptno,10,'1',20,'2',30,'3','??') from dept;
DEPTNO DECO
---------- ----
10 1
20 2
30 3
40 ??
SELECT product_id,
DECODE (warehouse_id, 1, 'Southlake',
2, 'San Francisco',
3, 'New Jersey',
4, 'Seattle',
'Non domestic') "Location"
FROM oe.inventories
WHERE product_id < 1775
ORDER BY product_id, "Location";
PRODUCT_ID Location
---------- --------------------------
1729 New Jersey
1729 Non domestic
1729 Non domestic
1729 Non domestic
1729 Non domestic
1729 Non domestic
1733 New Jersey
1733 Non domestic
1733 Non domestic
1733 Non domestic
1733 Non domestic
1733 Non domestic
1733 San Francisco
1733 Seattle
1733 Southlake
1734 New Jersey
……
6.1.1 Case
[實現switch ..case 邏輯]注:CASE語句在處理類似問題就顯得非常靈活。當只是需要匹配少量數值時,用Decode更為簡潔。
OE@test>SELECT cust_last_name,
2 CASE credit_limit WHEN 100 THEN 'Low'
3 WHEN 5000 THEN 'High'
4 ELSE 'Medium' END AS credit
5 FROM customers
6 ORDER BY cust_last_name, credit;
CUST_LAST_NAME CREDIT
---------------------------------------- ------------
Adjani Medium
Adjani Medium
Alexander Medium
Alexander Medium
Altman High
Altman Medium
Altman Medium
Andrews Medium
Andrews Medium
Ashby High
Ashby High
Aykroyd Medium
Baldwin Medium
…….
7 聚合函數
8 Rollup/cube
rollup 按分組的第一個列進行統計和最后的小計
cube 按分組的所有列的進行統計和最后的小計
SCOTT@test>select deptno,job ,sum(sal) from emp group by deptno,job;
DEPTNO JOB SUM(SAL)
---------- ------------------ ----------
20 CLERK 1900
30 SALESMAN 5600
20 MANAGER 2975
30 CLERK 950
10 PRESIDENT 1000
30 MANAGER 2850
10 CLERK 1000
10 MANAGER 1000
20 ANALYST 6000
SCOTT@test>select deptno,job ,sum(sal) from emp group by rollup(deptno,job);
DEPTNO JOB SUM(SAL)
---------- ------------------ ----------
10 CLERK 1000
10 MANAGER 1000
10 PRESIDENT 1000
10 3000
20 CLERK 1900
20 ANALYST 6000
20 MANAGER 2975
20 10875
30 CLERK 950
30 MANAGER 2850
30 SALESMAN 5600
30 9400
23275
SCOTT@test>select deptno,job ,sum(sal) from emp group by rollup(job,deptno);
DEPTNO JOB SUM(SAL)
---------- ------------------ ----------
10 CLERK 1000
20 CLERK 1900
30 CLERK 950
CLERK 3850
20 ANALYST 6000
ANALYST 6000
10 MANAGER 1000
20 MANAGER 2975
30 MANAGER 2850
MANAGER 6825
30 SALESMAN 5600
SALESMAN 5600
10 PRESIDENT 1000
PRESIDENT 1000
23275
cube 產生組內所有列的統計和最后的小計
SCOTT@test>select deptno,job ,sum(sal) from emp group by cube(deptno,job);
DEPTNO JOB SUM(SAL)
---------- ------------------ ----------
23275
CLERK 3850
ANALYST 6000
MANAGER 6825
SALESMAN 5600
PRESIDENT 1000
10 3000
10 CLERK 1000
10 MANAGER 1000
10 PRESIDENT 1000
20 10875
20 CLERK 1900
20 ANALYST 6000
20 MANAGER 2975
30 9400
30 CLERK 950
30 MANAGER 2850
30 SALESMAN 5600
8.1.1 AVG(DISTINCT|ALL)
all表示對所有的值求平均值,distinct只對不同的值求平均值
HR@test>SELECT AVG(salary) "Average"
2 FROM employees;
Average
----------
6461.83178
8.1.2 MAX(DISTINCT|ALL)
求最大值,ALL表示對所有的值求最大值,DISTINCT表示對不同的值求最大值,相同的只取一次
HR@test>SELECT MAX(salary) "Maximum"
2 FROM employees;
Maximum
----------
24000
8.1.3 MIN(DISTINCT|ALL)
求最小值,ALL表示對所有的值求最小值,DISTINCT表示對不同的值求最小值,相同的只取一次
HR@test>SELECT MIN(hire_date) "Earliest"
2 FROM employees;
Earliest
-----------------------
13-JAN-2001 00:00:00
8.1.4 COUNT
功能描述:對一組內發生的事情進行累積計數,如果指定*或一些非空常數,count將對所有行計數,如果指定一個表達式,count返回表達式非空賦值的計數,當有相同值出現時,這些相等的值都會被納入被計算的值;可以使用DISTINCT來記錄去掉一組中完全相同的數據后出現的行數。.
整個結果集是一個組
1) 求部門30 的最高工資,最低工資,平均工資,總人數,有工作的人數,工種數量及工資總和
SCOTT@test> select max(ename),max(sal),
2 min(ename),min(sal),
3 avg(sal),
4 count(*) ,count(job),count(distinct(job)) ,
5 sum(sal) from emp where deptno=30;
MAX(ENAME) MAX(SAL) MIN(ENAME) MIN(SAL) AVG(SAL) COUNT(*) COUNT(JOB) COUNT(DISTINCT(JOB)) SUM(SAL)
-------------------- ---------- -------------------- ---------- ---------- ---------- ---------- -------------------- ----------
WARD 2850 ALLEN 950 1566.66667 6 6 3 9400
9 分析函數
9.1 分析函數語法:
analytic_function([ arguments ]) OVER ([ query_partition_clause ] [ order_by_clause [ windowing_clause ] ])
例:
sum(sal) over (partition by deptno order by ename) new_alias
sum就是函數名,(sal)是分析函數的參數,每個函數有0~3個參數,參數可以是表達式,例如:sum(sal+comm),over 是一個關鍵字,用於標識分析函數,否則查詢分析器不能區別sum()聚集函數和sum()分析函數,partition by deptno 是可選的分區子句,如果不存在任何分區子句,則全部的結果集可看作一個單一的大區。order by ename 是可選的order by 子句,有些函數需要它,有些則不需要.依靠已排序數據的那些函數,如:用於訪問結果集中前一行和后一行的LAG和LEAD,必須使用,其它函數,如AVG,則不需要.在使用了任何排序的開窗函數時,該子句是強制性的,它指定了在計算分析函數時一組內的數據是如何排序的.
10 FUNCTION子句
ORACLE提供了26個分析函數,按功能分5類
分析函數分類
等級(ranking)函數:用於尋找前N種查詢
開窗(windowing)函數:用於計算不同的累計,如SUM,COUNT,AVG,MIN,MAX等,作用於數據的一個窗口上
例:
sum(t.sal) over (order by t.deptno,t.ename) running_total,
sum(t.sal) over (partition by t.deptno order by t.ename) department_total
制表(reporting)函數:與開窗函數同名,作用於一個分區或一組上的所有列
例:
sum(t.sal) over () running_total2,
sum(t.sal) over (partition by t.deptno ) department_total2
制表函數與開窗函數的關鍵不同之處在於OVER語句上缺少一個ORDER BY子句!
LAG,LEAD函數:這類函數允許在結果集中向前或向后檢索值,為了避免數據的自連接,它們是非常用用的.
VAR_POP,VAR_SAMP,STDEV_POPE及線性的衰減函數:計算任何未排序分區的統計值
Oracle 11g的分析函數
AVG *
CORR *
COUNT *
COVAR_POP *
COVAR_SAMP *
CUME_DIST
DENSE_RANK
FIRST
FIRST_VALUE *
LAG
LAST
LAST_VALUE *
LEAD
LISTAGG
MAX *
MIN *
NTH_VALUE *
NTILE
PERCENT_RANK
PERCENTILE_CONT
PERCENTILE_DISC
RANK
RATIO_TO_REPORT
REGR_ (Linear Regression) Functions *
ROW_NUMBER
STDDEV *
STDDEV_POP *
STDDEV_SAMP *
SUM *
VAR_POP *
VAR_SAMP *
VARIANCE *
10.1.1 PARTITION子句
按照表達式分區(就是分組),如果省略了分區子句,則全部的結果集被看作是一個單一的組
10.1.2 ORDER BY子句
分析函數中ORDER BY的存在將添加一個默認的開窗子句,這意味着計算中所使用的行的集合是當前分區中當前行和前面所有行,沒有ORDER BY時,默認的窗口是全部的分區 在Order by 子句后可以添加nulls last,如:order by comm desc nulls last 表示排序時忽略comm列為空的行.
10.1.3 WINDOWING子句
用於定義分析函數將在其上操作的行的集合
Windowing子句給出了一個定義變化或固定的數據窗口的方法,分析函數將對這些數據進行操作.默認的窗口是一個固定的窗口,僅僅在一組的第一行開始,一直繼續到當前行,要使用窗口,必須使用ORDER BY子句.根據2個標准可以建立窗口:數據值的范圍(RANGES)或與當前行的行偏移量.
5)Rang窗口
RANGE窗口僅對NUMBERS和DATES起作用,因為不可能從VARCHAR2中增加或減去N個單元
另外的限制是ORDER BY中只能有一列,因而范圍實際上是一維的,不能在N維空間中
6)Row窗口
利用ROW分區,就沒有RANGE分區那樣的限制了,數據可以是任何類型,且ORDER BY 可以包括很多列
7)Specifying窗口
UNBOUNDED PRECEDING:這個窗口從當前分區的每一行開始,並結束於正在處理的當前行
CURRENT ROW:該窗口從當前行開始(並結束)
Numeric Expression PRECEDING:對該窗口從當前行之前的數字表達式(Numeric Expression)的行開始,對RANGE來說,從行序值小於數字表達式的當前行的值開始.
Numeric Expression FOLLOWING:該窗口在當前行Numeric Expression行之后的行終止(或開始),且從行序值大於當前行Numeric Expression行的范圍開始(或終止)
rows between 100 preceding and 100 following:當前行100前,當前后100后
over(order by salary) 按照salary排序進行累計,order by是個默認的開窗函數
over(partition by deptno)按照部門分區
over(order by salary range between 50 preceding and 150 following)
每行對應的數據窗口是之前行幅度值不超過50,之后行幅度值不超過150 范圍[current_value-50, current_value+150]
over(order by salary rows between 50 preceding and 150 following)
每行對應的數據窗口是之前50行,之后150行
over(order by salary rows between unbounded preceding and unbounded following)
每行對應的數據窗口是從第一行到最后一行,等效:
over(order by salary range between unbounded preceding and unbounded following)
注意:分析函數允許你對一個數據集進排序和篩選,這是SQL從來不能實現的.除了最后的Order by子句之外,分析函數是在查詢中執行的最后的操作集,這樣的話,就不能直接在謂詞中使用分析函數,即不能在上面使用where或having子句!!!
10.2 排名
Oracle從8i開始就提供了3個排名函數:rand,dense_rank,row_number
Rank,Dense_rank,Row_number函數為每條記錄產生一個從1開始至N的自然數,N的值可能小於等於記錄的總數。這3個函數的唯一區別在於當碰到相同數據時的排名策略。
①ROW_NUMBER:
Row_number函數返回一個唯一的值,當碰到相同數據時,排名按照記錄集中記錄的順序依次遞增。
②DENSE_RANK:
Dense_rank函數返回一個唯一的值,除非當碰到相同數據時,此時所有相同數據的排名都是一樣的。
③RANK:
Rank函數返回一個唯一的值,除非遇到相同的數據時,此時所有相同數據的排名是一樣的,同時會在最后一條相同記錄和下一條不同記錄的排名之間空出排名。
SELECT department_id, last_name, salary,
RANK() OVER (PARTITION BY department_id ORDER BY salary) RANK,
DENSE_RANK() OVER (PARTITION BY department_id ORDER BY salary) DENSE_RANK,
ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary) ROW_NUMBER
FROM employees WHERE department_id = 50
ORDER BY RANK, last_name;
DEPARTMENT_ID LAST_NAME SALARY RANK DENSE_RANK ROW_NUMBER
------------- --------------- ---------- ---------- ---------- ----------
50 Olson 2100 1 1 1
50 Markle 2200 2 2 2
50 Philtanker 2200 2 2 3
50 Gee 2400 4 3 4
50 Landry 2400 4 3 5
50 Marlow 2500 6 4 6
50 Vargas 2500 6 4 7
50 Sullivan 2500 6 4 8
50 Perkins 2500 6 4 9
50 Patel 2500 6 4 10
50 OConnell 2600 11 5 11
50 Grant 2600 11 5 12
50 Matos 2600 11 5 13
50 Seo 2700 14 6 14
50 Mikkilineni 2700 14 6 15
50 Geoni 2800 16 7 16
50 Atkinson 2800 16 7 17
50 Jones 2800 16 7 18
…….
請注意上面的綠色高亮部分,這里生動的演示了3種不同的排名策略:
Rank
Dense_rank
Row_number
不同值排名
1,2,3,4,…..
1,2,3,4,…..
1,2,3,4,…..
有相同值排名
1,2,2,4,4,…..
1,2,2,3,3,…..
1,2,3,4,5…..
存在斷層
順序遞增
順序遞增
比較上面3種不同的策略,我們在選擇的時候就要根據客戶的需求來定奪了:
①假如只需要指定數目的記錄,那么采用row_number是最簡單的,但有漏掉的記錄的危險
②假如需要所有達到排名水平的記錄,那么采用rank或dense_rank是不錯的選擇。至於選擇哪一種則看客戶的需要,選擇dense_rank或得到最大的記錄
10.3 First/Last
排名查詢
功能描述:從DENSE_RANK返回的集合中取出排在最前面的一個值的行(可能多行,因為值可能相等),因此完整的語法需要在開始處加上一個集合函數以從中取出記錄
SAMPLE:下面例子中DENSE_RANK按部門分區,再按佣金commission_pct排序,FIRST取出佣金最低的對應的所有行,然后前面的MIN函數從這個集合中取出薪水最低的值;LAST取出佣金最高的對應的所有行,然后前面的MAX函數從這個集合中取出薪水最高的值
SELECT last_name, department_id, salary,
MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY commission_pct)
OVER (PARTITION BY department_id) "Worst",
MAX(salary) KEEP (DENSE_RANK LAST ORDER BY commission_pct)
OVER (PARTITION BY department_id) "Best"
FROM employees
ORDER BY department_id, salary, last_name;
LAST_NAME DEPARTMENT_ID SALARY Worst Best
--------------- ------------- ---------- ---------- ----------
Whalen 10 4400 4400 4400
Fay 20 6000 6000 13000
Hartstein 20 13000 6000 13000
Colmenares 30 2500 2500 11000
Himuro 30 2600 2500 11000
Tobias 30 2800 2500 11000
Baida 30 2900 2500 11000
Khoo 30 3100 2500 11000
Raphaely 30 11000 2500 11000
Mavris 40 6500 6500 6500
Olson 50 2100 2100 8200
Markle 50 2200 2100 8200
Philtanker 50 2200 2100 8200
10.4 ntile
將列值分為幾個等級。
HR@test>SELECT last_name, salary, NTILE(4) OVER (ORDER BY salary DESC) AS quartile
2 FROM employees
3 WHERE department_id = 100
4 ORDER BY quartile;
LAST_NAME SALARY QUARTILE
--------------- ---------- ----------
Greenberg 12008 1
Faviet 9000 1
Chen 8200 2
Urman 7800 2
Sciarra 7700 3
Popp 6900 4
10.5 first_value/last_value:
功能描述:返回組中數據窗口的第一個值。
SAMPLE:下面例子計算80部門分區按薪水排序的數據窗口的第一個值對應的名字,如果薪水的第一個值有多個,則從多個對應的名字中取缺省排序的第一個名字
SELECT department_id, last_name, salary,
FIRST_VALUE(last_name) OVER (ORDER BY salary ROWS UNBOUNDED PRECEDING) AS lowest_sal,last_VALUE(last_name) OVER (ORDER BY salary range between unbounded preceding and unbounded following) AS great_sal
FROM (SELECT * FROM employees
WHERE department_id = 80
ORDER BY employee_id)
ORDER BY salary;
DEPARTMENT_ID LAST_NAME SALARY LOWEST_SAL GREAT_SAL
------------- --------------- ---------- -------------------------------------------------- --------------------------------------------------
80 Kumar 6100 Kumar Russell
80 Banda 6200 Kumar Russell
80 Johnson 6200 Kumar Russell
80 Ande 6400 Kumar Russell
80 Lee 6800 Kumar Russell
…….
80 Russell 14000 Kumar Russell
10.6 lag、lead
功能描述:可以訪問結果集中的其它行而不用進行自連接。它允許去處理游標,就好像游標是一個數組一樣。在給定組中可參考當前行之前的行,這樣就可以從組中與當前行一起選擇以前的行。Offset是一個正整數,其默認值為1,若索引超出窗口的范圍,就返回默認值(默認返回的是組中第一行),其相反的函數是LEAD
HR@test>select first_name,last_name,hire_date, lag(hire_date,1,'1987-01-01') over(order by hire_date) as prev_hire_date,hire_date-lag(hire_date,1,'1987-01-01') over(order by hire_date) as days_between_hires from hr.employees order by hire_date;
FIRST_NAME LAST_NAME HIRE_DATE PREV_HIRE_ DAYS_BETWEEN_HIRES
---------------------------------------- --------------- ---------- ---------- ------------------
Lex De Haan 2001-01-13 1987-01-01 5126
Susan Mavris 2002-06-07 2001-01-13 510
Hermann Baer 2002-06-07 2002-06-07 0
Shelley Higgins 2002-06-07 2002-06-07 0
William Gietz 2002-06-07 2002-06-07 0
Daniel Faviet 2002-08-16 2002-06-07 70
Nancy Greenberg 2002-08-17 2002-08-16 1
Den Raphaely 2002-12-07 2002-08-17 112
Payam Kaufling 2003-05-01 2002-12-07 145
Alexander Khoo 2003-05-18 2003-05-01 17
Steven King 2003-06-17 2003-05-18 30
Renske Ladwig 2003-07-14 2003-06-17 27
Jennifer Whalen 2003-09-17 2003-07-14 65
10.7 RATIO_TO_REPORT
HR@test>HR@test>SELECT last_name, salary, RATIO_TO_REPORT(salary) OVER () AS rr
2 FROM employees
3 WHERE job_id = 'PU_CLERK'
4 ORDER BY last_name, salary, rr;
LAST_NAME SALARY RR
--------------- ---------- ----------
Baida 2900 .208633094
Colmenares 2500 .179856115
Himuro 2600 .18705036
Khoo 3100 .223021583
Tobias 2800 .201438849
10.8 AVG /count/sum
功能描述:用於計算一個組和數據窗口內表達式的平均值。
SAMPLE:下面的例子中列c_mavg計算員工表中每個員工的平均薪水報告,該平均值由當前員工和與之具有相同經理的前一個和后一個三者的平均數得來;
OE@test>OE@test>SELECT manager_id, last_name, hire_date, salary,
2 AVG(salary) OVER (PARTITION BY manager_id ORDER BY hire_date
3 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS c_mavg
4 FROM employees;
MANAGER_ID LAST_NAME HIRE_DATE SALARY C_MAVG
---------- --------------- ----------------------- ---------- ----------
100 De Haan 13-JAN-2001 00:00:00 17000 14000
100 Raphaely 07-DEC-2002 00:00:00 11000 11966.6667
100 Kaufling 01-MAY-2003 00:00:00 7900 10633.3333
100 Hartstein 17-FEB-2004 00:00:00 13000 9633.33333
100 Weiss 18-JUL-2004 00:00:00 8000 11666.6667
100 Russell 01-OCT-2004 00:00:00 14000 11833.3333
100 Partners 05-JAN-2005 00:00:00 13500 13166.6667
100 Errazuriz 10-MAR-2005 00:00:00 12000 11233.3333
100 Fripp 10-APR-2005 00:00:00 8200 12400
100 Kochhar 21-SEP-2005 00:00:00 17000 10566.6667
100 Vollman 10-OCT-2005 00:00:00 6500 11500
100 Cambrault 15-OCT-2007 00:00:00 11000 7766.66667
100 Mourgos 16-NOV-2007 00:00:00 5800 9100
100 Zlotkey 29-JAN-2008 00:00:00 10500 8150
101 Baer 07-JUN-2002 00:00:00 10000 11004
101 Higgins 07-JUN-2002 00:00:00 12008 9502.66667
SAMPLE:下面例子中計算每個員工在按薪水排序中當前行附近薪水在[n-50,n+150]之間的行數,n表示當前行的薪水
例如,Philtanker的薪水2200,排在他之前的行中薪水大於等於2200-50的有1行,排在他之后的行中薪水小於等於2200+150的行沒有,所以count計數值cnt3為2(包括自己當前行);cnt2值相當於小於等於當前行的SALARY值的所有行數
OE@test>SELECT last_name, salary, COUNT(*) OVER () AS cnt1,COUNT(*) OVER (ORDER BY salary) AS cnt2,COUNT(*) OVER (ORDER BY salary RANGE BETWEEN 50 PRECEDING AND 150 FOLLOWING) AS cnt3 FROM employees;
LAST_NAME SALARY CNT1 CNT2 CNT3
--------------- ---------- ---------- ---------- ----------
Olson 2100 107 1 3
Philtanker 2200 107 3 2
Markle 2200 107 3 2
Landry 2400 107 5 8
Gee 2400 107 5 8
Perkins 2500 107 11 10
Colmenares 2500 107 11 10
Patel 2500 107 11 10
Vargas 2500 107 11 10
Sullivan 2500 107 11 10
Marlow 2500 107 11 10
OConnell 2600 107 15 6
Grant 2600 107 15 6
Himuro 2600 107 15 6
Matos 2600 107 15 6
Mikkilineni 2700 107 17 6
Seo 2700 107 17 6
Tobias 2800 107 21 7
Geoni 2800 107 21 7
Atkinson 2800 107 21 7
Jones 2800 107 21 7
Baida 2900 107 24 5
11 其它問題
12 null排序的處理
SCOTT@test>select empno,ename,mgr,sal,comm from emp order by comm desc nulls last;
EMPNO ENAME MGR SAL COMM
---------- -------------------- ---------- ---------- ----------
7654 MARTIN 7698 1250 1400
7521 WARD 7698 1250 500
7499 ALLEN 7698 1600 300
7844 TURNER 7698 1500 0
7788 SCOTT 7566 3000
7839 KING 1000
7876 ADAMS 7788 1100
7900 JAMES 7698 950
7902 FORD 7566 3000
7934 MILLER 7782 1000
7698 BLAKE 7839 2850
7566 JONES 7839 2975
7369 SMITH 7902 800
7782 CLARK 7839 1000
SCOTT@test>select empno,ename,mgr,sal,comm from emp order by comm; ---默認
EMPNO ENAME MGR SAL COMM
---------- -------------------- ---------- ---------- ----------
7844 TURNER 7698 1500 0
7499 ALLEN 7698 1600 300
7521 WARD 7698 1250 500
7654 MARTIN 7698 1250 1400
7788 SCOTT 7566 3000
7839 KING 1000
7876 ADAMS 7788 1100
7900 JAMES 7698 950
7902 FORD 7566 3000
7934 MILLER 7782 1000
7698 BLAKE 7839 2850
7566 JONES 7839 2975
7369 SMITH 7902 800
7782 CLARK 7839 1000
12.1.1 count中null 的處理
count(*)null值在內所有行
count(comssion_pct) 不包括null值
OE@test>select count(*),count(commission_pct),avg(commission_pct),sum(commission_pct)/count(commission_pct) from hr.employees;
COUNT(*) COUNT(COMMISSION_PCT) AVG(COMMISSION_PCT) SUM(COMMISSION_PCT)/COUNT(COMMISSION_PCT)
---------- --------------------- ------------------- -----------------------------------------
107 35 .222857143 .222857143
13 LISTAGG
LISTAGG(measure_expr [, 'delimiter']) WITHIN GROUP (order_by_clause) [OVER query_partition_clause]
按照order by 的排序,順序連接measure_expr列的值,delimiter 缺省為null
HR@test>SELECT LISTAGG(last_name, '; ')
2 WITHIN GROUP (ORDER BY hire_date, last_name) "Emp_list",
3 MIN(hire_date) "Earliest"
4 FROM employees
5 WHERE department_id = 30;
Emp_list Earliest
--------------------------------------------------------------------------------------- -----------------------
Raphaely; Khoo; Tobias; Baida; Himuro; Colmenares 07-DEC-2002 00:00:00
HR@test>SELECT hire_date, last_name
2 FROM employees
3 WHERE department_id = 30 order by hire_date, last_name;
HIRE_DATE LAST_NAME
----------------------- --------------------------------------------------
07-DEC-2002 00:00:00 Raphaely
18-MAY-2003 00:00:00 Khoo
24-JUL-2005 00:00:00 Tobias
24-DEC-2005 00:00:00 Baida
15-NOV-2006 00:00:00 Himuro
10-AUG-2007 00:00:00 Colmenares
分組
HR@test>SELECT department_id "Dept.",
2 LISTAGG(last_name, '; ') WITHIN GROUP (ORDER BY hire_date) "Employees"
3 FROM employees
4 GROUP BY department_id
5 ORDER BY department_id;
Dept. Employees
---------- --------------------------------------------------
10 Whalen
20 Hartstein; Fay
30 Raphaely; Khoo; Tobias; Baida; Himuro; Colmenares
40 Mavris
50 Kaufling; Ladwig; Rajs; Sarchand; Bell; Mallin; We
iss; Davies; Marlow; Bull; Everett; Fripp; Chung;
Nayer; Dilly; Bissot; Vollman; Stiles; Atkinson; T
aylor; Seo; Fleaur; Matos; Patel; Walsh; Feeney; D
ellinger; McCain; Vargas; Gates; Rogers; Mikkiline
ni; Landry; Cabrio; Jones; Olson; OConnell; Sulliv
an; Mourgos; Gee; Perkins; Grant; Geoni; Philtanke
r; Markle
60 Austin; Hunold; Pataballa; Lorentz; Ernst
70 Baer
80 King; Sully; Abel; McEwen; Russell; Partners; Tuck
er; Errazuriz; Smith; Ozer; Hutton; Bernstein; Hal
l; Vishney; Doran; Fox; Bloom; Taylor; Olsen; Livi
ngston; Sewall; Cambrault; SMITH; Greene; Bates; C
ambrault; Tuvault; Johnson; Marvins; Zlotkey; Lee;
Ande; Banda; Kumar
90 De Haan; King; Kochhar
100 Faviet; Greenberg; Chen; Sciarra; Urman; Popp
110 Gietz; Higgins
Grant
SELECT department_id "Dept", hire_date "Date", last_name "Name",
LISTAGG(last_name, '; ') WITHIN GROUP (ORDER BY hire_date, last_name)
OVER (PARTITION BY department_id) as "Emp_list"
FROM employees
ORDER BY "Dept", "Date", "Name";
Dept Date Name Emp_list
---------- ----------------------- -------------------- --------------------------------------------------
10 17-SEP-2003 00:00:00 Whalen Whalen
20 17-FEB-2004 00:00:00 Hartstein Hartstein; Fay
20 17-AUG-2005 00:00:00 Fay Hartstein; Fay
30 07-DEC-2002 00:00:00 Raphaely Raphaely; Khoo; Tobias; Baida; Himuro; Colmenares
30 18-MAY-2003 00:00:00 Khoo Raphaely; Khoo; Tobias; Baida; Himuro; Colmenares
30 24-JUL-2005 00:00:00 Tobias Raphaely; Khoo; Tobias; Baida; Himuro; Colmenares
30 24-DEC-2005 00:00:00 Baida Raphaely; Khoo; Tobias; Baida; Himuro; Colmenares
30 15-NOV-2006 00:00:00 Himuro Raphaely; Khoo; Tobias; Baida; Himuro; Colmenares
30 10-AUG-2007 00:00:00 Colmenares Raphaely; Khoo; Tobias; Baida; Himuro; Colmenares
40 07-JUN-2002 00:00:00 Mavris Mavris
……
14 行列轉換: Pivot 行轉列
OE@test>select customer_id,product_id,quantity from orders join order_items using(order_id) where product_id in (3170 ,3176 ,3182,3163 ,3165 ) ; ---五個產品的銷售情況
CUSTOMER_ID PRODUCT_ID QUANTITY
----------- ---------- ----------
101 3163 142
101 3163 66
104 3182 77
104 3176 72
104 3170 70
104 3165 64
104 3163 61
106 3176 62
106 3163 55
107 3165 76
108 3163 45
108 3165 31
109 3182 115
109 3165 112
116 3165 10
116 3163 5
116 3176 24
116 3170 24
116 3170 24
117 3165 67
117 3163 63
118 3170 42
119 3170 36
求這五個產品中每個客戶購買情況。
with order_item_query as (select customer_id,product_id,quantity from orders join order_items using(order_id)) select * from oe.order_item_query pivot ( sum(quantity) as sum_qty for (product_id) in(3170 as P3170,3176 as P3176,3182 as P3182,3163 as P3163,3165 as P3165)) order by customer_id;
CUSTOMER_ID P3170_SUM_QTY P3176_SUM_QTY P3182_SUM_QTY P3163_SUM_QTY P3165_SUM_QTY
----------- ------------- ------------- ------------- ------------- -------------
101 208
102
103
104 70 72 77 61 64
105
106 62 55
107 76
108 45 31
109 115 112
116 48 24 5 10
117 63 67
14.2 Unpivot : 列轉行
OE@test>select * from email_signup;
USER_ACCOUNT SIGNUP_DATE USER_EMAIL FRIEND1_EMAIL FRIEND2_EMAIL FRIEND3_EMAIL
-------------------- ----------------------- --------------- --------------- --------------- ---------------
lakala 18-MAR-2014 00:00:00 123@lakala.com 1@lakala.com 2@lakala.com 3@lakala.com
lkl 17-MAR-2014 00:00:00 p123@lakala.com p1@lakala.com p2@lakala.com p3@lakala.com
OE@test>select user_account, signup_date, src_col_name, friend_email
2 from email_signup
3 unpivot (
4 (friend_email) for src_col_name
5 in (user_email, friend1_email, friend2_email, friend3_email)
6 )
7 ;
USER_ACCOUNT SIGNUP_DATE SRC_COL_NAME FRIEND_EMAIL
-------------------- ----------------------- -------------------------- --------------------
lakala 18-MAR-2014 00:00:00 USER_EMAIL 123@lakala.com
lakala 18-MAR-2014 00:00:00 FRIEND1_EMAIL 1@lakala.com
lakala 18-MAR-2014 00:00:00 FRIEND2_EMAIL 2@lakala.com
lakala 18-MAR-2014 00:00:00 FRIEND3_EMAIL 3@lakala.com
lkl 17-MAR-2014 00:00:00 USER_EMAIL p123@lakala.com
lkl 17-MAR-2014 00:00:00 FRIEND1_EMAIL p1@lakala.com
lkl 17-MAR-2014 00:00:00 FRIEND2_EMAIL p2@lakala.com
lkl 17-MAR-2014 00:00:00 FRIEND3_EMAIL p3@lakala.com
