目錄
===============================================
1.帶空值的排列
2.Top/Bottom N查詢
3.First/Last排名查詢
4.按層次查詢
一、帶空值的排列:
在前面《Oracle開發專題之:分析函數2(Rank、Dense_rank、row_number)》一文中,我們已經知道了如何為一批記錄進行全排列、分組排列。假如被排列的數據中含有空值呢?
SQL> select region_id, customer_id,
2 sum(customer_sales) cust_sales,
3 sum(sum(customer_sales)) over(partition by region_id) ran_total,
4 rank() over(partition by region_id
5 order by sum(customer_sales) desc) rank
6 from user_order
7 group by region_id, customer_id;

REGION_ID CUSTOMER_ID CUST_SALES RAN_TOTAL RANK
---------- ----------- ---------- ---------- ----------
10 31 6238901 1
10 26 1808949 6238901 2
10 27 1322747 6238901 3
10 30 1216858 6238901 4
10 28 986964 6238901 5
10 29 903383 6238901 6
我們看到這里有一條記錄的CUST_TOTAL字段值為NULL,但居然排在第一名了!顯然這不符合情理。所以我們重新調整完善一下我們的排名策略,看看下面的語句:
SQL> select region_id, customer_id,
2 sum(customer_sales) cust_total,
3 sum(sum(customer_sales)) over(partition by region_id) reg_total,
4 rank() over(partition by region_id
order by sum(customer_sales) desc NULLS LAST) rank
5 from user_order
6 group by region_id, customer_id;

REGION_ID CUSTOMER_ID CUST_TOTAL REG_TOTAL RANK
---------- ----------- ---------- ---------- ----------
10 26 1808949 6238901 1
10 27 1322747 6238901 2
10 30 1216858 6238901 3
10 28 986964 6238901 4
10 29 903383 6238901 5
10 31 6238901 6
綠色高亮處,NULLS LAST/FIRST告訴Oracle讓空值排名最后后第一。
注意是NULLS,不是NULL。
二、Top/Bottom N查詢:
在日常的工作生產中,我們經常碰到這樣的查詢:找出排名前5位的訂單客戶、找出排名前10位的銷售人員等等。現在這個對我們來說已經是很簡單的問題了。下面我們用一個實際的例子來演示:
【1】找出所有訂單總額排名前3的大客戶:
SQL> select *
SQL> from (select region_id,
SQL> customer_id,
SQL> sum(customer_sales) cust_total,
SQL> rank() over(order by sum(customer_sales) desc NULLS LAST) rank
SQL> from user_order
SQL> group by region_id, customer_id)
SQL> where rank <= 3;

REGION_ID CUSTOMER_ID CUST_TOTAL RANK
---------- ----------- ---------- ----------
9 25 2232703 1
8 17 1944281 2
7 14 1929774 3

SQL>
【2】找出每個區域訂單總額排名前3的大客戶:
SQL> select *
2 from (select region_id,
3 customer_id,
4 sum(customer_sales) cust_total,
5 sum(sum(customer_sales)) over(partition by region_id) reg_total,
6 rank() over(partition by region_id
order by sum(customer_sales) desc NULLS LAST) rank
7 from user_order
8 group by region_id, customer_id)
9 where rank <= 3;

REGION_ID CUSTOMER_ID CUST_TOTAL REG_TOTAL RANK
---------- ----------- ---------- ---------- ----------
5 4 1878275 5585641 1
5 2 1224992 5585641 2
5 5 1169926 5585641 3
6 6 1788836 6307766 1
6 9 1208959 6307766 2
6 10 1196748 6307766 3
7 14 1929774 6868495 1
7 13 1310434 6868495 2
7 15 1255591 6868495 3
8 17 1944281 6854731 1
8 20 1413722 6854731 2
8 18 1253840 6854731 3
9 25 2232703 6739374 1
9 23 1224992 6739374 2
9 24 1224992 6739374 2
10 26 1808949 6238901 1
10 27 1322747 6238901 2
10 30 1216858 6238901 3

18 rows selected.
三、First/Last排名查詢:
想象一下下面的情形:找出訂單總額最多、最少的客戶。按照前面我們學到的知識,這個至少需要2個查詢。第一個查詢按照訂單總額降序排列以期拿到第一名,第二個查詢按照訂單總額升序排列以期拿到最后一名。是不是很煩?因為Rank函數只告訴我們排名的結果,卻無法自動替我們從中篩選結果。
幸好Oracle為我們在排列函數之外提供了兩個額外的函數:first、last函數,專門用來解決這種問題。還是用實例說話:
SQL> select min(customer_id)
2 keep (dense_rank first order by sum(customer_sales) desc) first,
3 min(customer_id)
4 keep (dense_rank last order by sum(customer_sales) desc) last
5 from user_order
6 group by customer_id;

FIRST LAST
---------- ----------
31 1
這里有幾個看起來比較疑惑的地方:
①為什么這里要用min函數
②Keep這個東西是干什么的
③fist/last是干什么的
④dense_rank和dense_rank()有什么不同,能換成rank嗎?
首先解答一下第一個問題:min函數的作用是用于當存在多個First/Last情況下保證返回唯一的記錄。假如我們去掉會有什么樣的后果呢?
SQL> select keep (dense_rank first order by sum(customer_sales) desc) first,
2 keep (dense_rank last order by sum(customer_sales) desc) last
3 from user_order
4 group by customer_id;
select keep (dense_rank first order by sum(customer_sales) desc) first,
*
ERROR at line 1:
ORA-00907: missing right parenthesis
接下來看看第2個問題:keep是干什么用的?從上面的結果我們已經知道Oracle對排名的結果只“保留”2條數據,這就是keep的作用。告訴Oracle只保留符合keep條件的記錄。
那么什么才是符合條件的記錄呢?這就是第3個問題了。dense_rank是告訴Oracle排列的策略,first/last則告訴最終篩選的條件。
第4個問題:如果我們把dense_rank換成rank呢?
SQL> select min(region_id)
2 keep(rank first order by sum(customer_sales) desc) first,
3 min(region_id)
4 keep(rank last order by sum(customer_sales) desc) last
5 from user_order
6 group by region_id;
select min(region_id)
*
ERROR at line 1:
ORA-02000: missing DENSE_RANK
四、按層次查詢:
現在我們已經見識了如何通過Oracle的分析函數來獲取Top/Bottom N,第一個,最后一個記錄。有時我們會收到類似下面這樣的需求:找出訂單總額排名前1/5的客戶。
很熟悉是不?我們馬上會想到第二點中提到的方法,可是rank函數只為我們做好了排名,并不知道每個排名在總排名中的相對位置,這時候就引入了另外一個分析函數NTile,下面我們就以上面的需求為例來講解一下:
SQL> select region_id,
2 customer_id,
3 ntile(5) over(order by sum(customer_sales) desc) til
4 from user_order
5 group by region_id, customer_id;

REGION_ID CUSTOMER_ID TILE
---------- ----------- ----------
10 31 1
9 25 1
10 26 1
6 6 1
8 18 2
5 2 2
9 23 3
6 9 3
7 11 3
5 3 4
6 8 4
8 16 4
6 7 5
10 29 5
5 1 5
Ntil函數為各個記錄在記錄集中的排名計算比例,我們看到所有的記錄被分成5個等級,那么假如我們只需要前1/5的記錄則只需要截取TILE的值為1的記錄就可以了。假如我們需要排名前25%的記錄(也就是1/4)那么我們只需要設置ntile(4)就可以了。
參考資料:《Mastering Oracle SQL》(By Alan Beaulieu, Sanjay Mishra O'Reilly June 2004 0-596-00632-2)
-------------------------------------------------------------
生活就像打牌,不是要抓一手好牌,而是要盡力打好一手爛牌。
posted on 2008-06-27 09:44
Paul Lin 閱讀(6604)
評論(1) 編輯 收藏 所屬分類:
Oracle 開發