posts - 262,  comments - 221,  trackbacks - 0

          目錄
          ===============================================

          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) desclast
            
          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(5over(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 閱讀(6607) 評論(1)  編輯  收藏 所屬分類: Oracle 開發


          FeedBack:
          # re: 【原】Oracle應用專題之:分析函數3(Top/Bottom N、First/Last、NTile)[未登錄]
          2008-09-10 11:56 | ty
          講的真是很清楚,非常不錯~!  回復  更多評論
            
          <2008年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          常用鏈接

          留言簿(21)

          隨筆分類

          隨筆檔案

          BlogJava熱點博客

          好友博客

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 三河市| 林甸县| 扶余县| 剑河县| 莱阳市| 盐津县| 永康市| 嘉义市| 土默特左旗| 怀仁县| 英山县| 和政县| 鹤岗市| 峡江县| 密山市| 大石桥市| 清流县| 烟台市| 盐山县| 阿图什市| 嘉善县| 秀山| 双流县| 郑州市| 高清| 曲麻莱县| 渝中区| 新昌县| 宾阳县| 阿拉善盟| 惠东县| 中超| 武宁县| 罗定市| 兴海县| 交口县| 罗源县| 宣恩县| 钟祥市| 桂平市| 新野县|