723
技術社區[雲棲]
阿裏雲大數據利器Maxcompute學習之--窗口函數實現分組TopN
看到很多用戶經常會問如何對分組內進行排序。
官方文檔:https://help.aliyun.com/document_detail/34994.html?spm=5176.doc27891.6.611.Q1bk3j
例如需求:
1. odps 裏麵能否做排名操作,比如一個表裏麵有 用戶ID 和 金額 兩個字段,用金額大小排序的話,我如何計算用戶的排名(金額最大的是 第一名 ,以此類推)
2. 計算每個金融產品的最大投資者,或者前幾名
類似這一類的需求,我們總結為實現分組內的排序,取TopN,那麼在hive中有兩個個函數可以分開實現
first_value: 取分組內排序後,截止到當前行,第一個值,
row_number():實現組內排序,並對組內行進行標記行號。
那在odps中沒有first_value這個函數,但是同樣可以實現top1的需求。下麵用一個大家最愛的公司員工表來舉例實現
員工表:
empno | ename | job | mgr | hiredate | sal | comm | deptno |
---|---|---|---|---|---|---|---|
7839 | KING | PRESIDENT | NULL | 1981-11-17 00:00:00 | 5000.0 | NULL | 10 |
7566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.0 | NULL | 20 |
7698 | BLAKE | MANAGER | 7839 | 1981-05-01 00:00:00 | 2850.0 | NULL | 30 |
7788 | SCOTT | ANALYST | 7566 | 1987-07-13 01:00:00 | 3000.0 | NULL | 20 |
7782 | CLARK | MANAGER | 7839 | 1981-06-09 00:00:00 | 2450.0 | NULL | 10 |
7369 | SMITH | CLERK | 7902 | 1980-12-17 00:00:00 | 800.0 | NULL | 20 |
7902 | FORD | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.0 | NULL | 20 |
7876 | ADAMS | CLERK | 7788 | 1987-07-13 01:00:00 | 1100.0 | NULL | 20 |
7900 | JAMES | CLERK | 7698 | 1981-12-03 00:00:00 | 950.0 | NULL | 30 |
7934 | MILLER | CLERK | 7782 | 1982-01-23 00:00:00 | 1300.0 | NULL | 10 |
7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 00:00:00 | 1600.0 | 300.0 | 30 |
7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 00:00:00 | 1250.0 | 1400.0 | 30 |
7844 | TURNER | SALESMAN | 7698 | 1981-09-08 00:00:00 | 1500.0 | 0.0 | 30 |
7521 | WARD | SALESMAN | 7698 | 1981-02-22 00:00:00 | 1250.0 | 500.0 | 30 |
使用row_number()對相同 job 的薪水sal 進行
排序,取組內最大,等不及了,直接上sql
```select * from (
select job,sal,
row_number() over(partition by job order by sal desc) as rn from emp
) a where rn=1;
//partition by 跟分組字段
//order by 跟排序字段+升降關鍵字 默認升序排列。
結果:
job | sal | rn |
-----|----------|------------
ANALYST | 3000.0 | 1
CLERK | 1300.0 | 1
MANAGER | 2975.0 | 1
PRESIDENT | 5000.0 | 1
SALESMAN | 1600.0 | 1
同理如果想實現topN,那把rn=1改成rn<N,例如我想看每個崗位job前兩個工資最多的人,sql如下
select * from (
select job,ename,sal,
row_number() over(partition by job order by sal desc) as rn from emp
) a where rn<3;
結果:
job | ename | sal | rn |
----|-------|----------|------------
ANALYST | SCOTT | 3000.0 | 1 |
ANALYST | FORD | 3000.0 | 2 |
CLERK | MILLER | 1300.0 | 1 |
CLERK | ADAMS | 1100.0 | 2 |
MANAGER | JONES | 2975.0 | 1 |
MANAGER | BLAKE | 2850.0 | 2 |
PRESIDENT | KING | 5000.0 | 1 |
SALESMAN | ALLEN | 1600.0 | 1 |
SALESMAN | TURNER | 1500.0 | 2 |
+-----+-------+------------+------------+
那這是一個簡單的例子。
對於類似需求可以用這個方法來實現TopN的計算。
注意:這種方法對於數量級不是很大的或者分組比較均勻的大數據量實用,
如果分組鍵值不均勻,導致單個或者幾個鍵值比較大,那會有數據傾斜的問題。此時我們可以從sql上優化寫法,例如可以排查哪幾個鍵值比較大單獨拉出來一個任務執行。
最後更新:2017-06-27 22:32:09