RANK() OVER和ROW_NUMBER() OVER的學習筆記


     近來在工作上遇到一件事情。我有一張用戶訂單表,這個訂單表有一個order_id,是唯一約束。同時有一張訂單流程表,和訂單表以ser_id關聯,一個ser_id至少對應一條訂單流程記錄。現在我要將兩個表匯總,成為一張表,以ser_id為唯一約束,其中一個字段來自流程表,這個字段是ser_id對應的幾條工作流程記錄中work_id最大的。

     大致上訂單表示這樣的:

     order_id    ser_id ......

     112333      100001

     122112      100001

     122882      100211

     ......

     而工作流程表是這樣的:

     work_id     ser_id ......

     91188       100001

     91198       100001

     91108       100001

     91223       100221

     ......

     最開始我的想法是使用rank() over,於是我寫了這樣一段:

SELECT 
A.*,
B.I_NAME
FROM ORDER_T A,
(SELECT X.*,
RANK() OVER(PARTITION BY ser_id ORDER BY X.ser_id) RK) B
WHERE A.SER_ID = B.SER_ID(+)
AND B.RK = 1;

      最開始的時候我以為這個是對的,但是后來發現這是個很白痴的SQL。為什么白痴下面慢慢講。

     下面是舉例說明,有兩張表,i_test作為訂單表,duibibiao作為工作項表,直接上圖:

圖1

圖2

      我理想的查詢結果是這樣的:

  

圖3

      因為我的目的是做一張中間表,這個中間表是我最后用來統計我們這個月接了多少單子的。按照我上面的SQL,執行出來的結果是這樣的:

     

      當時我還有點懵懂,不知道為什么會出現這種情況,不僅僅是100001對應的項多了那么多,而且還有幾條記錄不見了。后來我才發現,B.RK=1這一條就很白痴,因為之前做的乘積中,做了連接,這樣的話形成的臨時表中就會有一列的RK有的有數有的沒有數,這樣的話會把需要的數據過濾掉。我做連接查詢的目的就是保留所有的訂單表記錄,因為最后我是要count這個表,然后告訴老板這個月我們接收了多少訂單的。  order by的那個地方也很白痴,因為會有很多ser_id是重復的,因此很多個RK都是等於1的,這樣下來挑不出來單個的一條記錄,你怎么排序都不行。

      於是我把SQL改成了這樣:

      

SELECT A.SER_ID, B.I_NAME
FROM I_TEST A,
(SELECT *
FROM (SELECT X.WORK_ID,
X.SER_ID,
X.I_NAME,
RANK() OVER(PARTITION BY X.SER_ID ORDER BY X.ROWID) RK
FROM DUIBIBIAO X) Y
WHERE Y.RK = 1) B
WHERE A.SER_ID = B.SER_ID(+)

      坑爹的正確結果終於出來了。這里為了解決上面的那個問題,我用到了偽列ROWID,這個可沒有重復的,不管正序倒序,都能找到一條記錄。

      后來我上網的時候發現了一個有意思的東西,ROW_NUMBER() OVER。這個的好處就是不會產生序列號相同的情況。SQL如下:

      

  SELECT A.SER_ID, B.I_NAME
FROM I_TEST A,
(SELECT *
FROM (SELECT X.WORK_ID,
X.SER_ID,
X.I_NAME,
row_number() OVER(PARTITION BY X.SER_ID ORDER BY X.ser_id) RK
FROM DUIBIBIAO X) Y
WHERE Y.RK = 1) B
WHERE A.SER_ID = B.SER_ID(+)

       得到的結果也完全正確。現在我就想比比誰的效率更高點。因為RANK() OVER的速度實在是......

       祭出一個非常大的表,叫做test,有10000000+數據。這個表有兩個字段,ser_id和area_id,語句如下:

SELECT A.*,
ROW_NUMBER() OVER(PARTITION BY A.AREA_ID ORDER BY A.SER_ID DESC) RK
FROM TEST A;

SELECT a.*,
RANK() OVER(PARTITION BY a.area_id ORDER BY a.ser_id DESC) rk FROM TEST a;

       對比一下效率:

      沒有看到任何區別,都是一樣的慢。

      總結一下,不管是RANK還是ROW_NUMBER,都是非常好用的分析函數。我這里只是用到了很簡單的一部分,並且學習到了兩個函數的通用之處。但是不管怎么樣,只要你的表夠大,做一次分析是不可避免的會出現超級慢的情況,這種情況下除了拼機器的性能之外,如果邏輯正確,可以對要開窗分析的表進行一些限制,這樣的話會降低很大的成本。比如說我上面的SQL,如果我在業務中只是需要西安的數據,那么我可以在其中添加where t.area_id = 290。我試驗了一下,ROWS變成了5660K,BYTES變成了140M,TEMPSPC變成了195M,CPU COST變成了49918。

      歡迎大家批評指正。歡迎留言。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM