摘要
本文對Hive中常用的三個排序函式row_number()
、dense_rank()
、rank()
的特性進行類比和總結,並通過筆者親自動手寫的一個小實驗,直觀展現這三個函式的特點。
三個排序函式的共同點與區別
函式 | 共同點 | 不同點 |
row_number() | 用於特定場景下實現排序需求; 均從1開始排序 | 無重複排名(相同排名的按序排名) |
dense_rank() | 有相同排名,但不會跳過佔用的排名 | |
rank() | 有相同排名,但會跳過佔用的排名 |
實驗示例
set mapreduce.job.queuename=QueueA;
use STUDENT_DB;
--建立學生分數表
DROP TABLE IF EXISTS STUDENT_DB.SCORE_TABLE1;
CREATE TABLE IF NOT EXISTS STUDENT_DB.SCORE_TABLE1
(
ID STRING COMMENT '唯一ID',
NAME STRING COMMENT '姓名',
SCORE INT COMMENT '分數',
CLASS_NUM STRING COMMENT '班級編號'
)
COMMENT '學生分數表'
PARTITIONED BY (pt_dt STRING)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\27'
STORED AS ORCFILE;
--向學生分數表插入資料
INSERT OVERWRITE TABLE STUDENT_DB.SCORE_TABLE1 PARTITION(pt_dt='2019-12-12') VALUES
('1', '小明', 89, '1班'),
('2', '小紅', 90, '1班'),
('3', '小軍', 90, '1班'),
('4', '小胖', 91, '1班'),
('5', '小李', 87, '1班'),
('6', '小郭', 99, '1班');
--建立學生分數排序結果表
DROP TABLE IF EXISTS STUDENT_DB.SCORE_RANK_TABLE1;
CREATE TABLE IF NOT EXISTS STUDENT_DB.SCORE_RANK_TABLE1
(
ID STRING COMMENT '唯一ID',
NAME STRING COMMENT '姓名',
SCORE INT COMMENT '分數',
CLASS_NUM STRING COMMENT '班級編號',
ROW_NUMBERS STRING COMMENT 'ROW_NUMBER排序結果',
DENSE_RANKS STRING COMMENT 'DENSE_RANKS排序結果',
RANKS STRING COMMENT 'RANKS排序結果'
)
COMMENT '學生分數排序結果表'
PARTITIONED BY (pt_dt STRING)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\27'
STORED AS ORCFILE;
INSERT OVERWRITE TABLE STUDENT_DB.SCORE_RANK_TABLE1 PARTITION(pt_dt='2019-12-12')
SELECT ID,
NAME,
SCORE,
CLASS_NUM,
ROW_NUMBER() OVER(PARTITION BY CLASS_NUM ORDER BY SCORE DESC) AS ROW_NUMBERS,
DENSE_RANK() OVER(PARTITION BY CLASS_NUM ORDER BY SCORE DESC) AS DENSE_RANKS,
RANK() OVER(PARTITION BY CLASS_NUM ORDER BY SCORE DESC) AS RANKS
FROM STUDENT_DB.SCORE_RANK_TABLE1
WHERE pt_dt='2019-12-12';
SELECT ID,
NAME,
SCORE,
CLASS_NUM,
ROW_NUMBERS,
DENSE_RANKS,
RANKS,
pt_dt
FROM STUDENT_DB.SCORE_RANK_TABLE1
WHERE pt_dt='2019-12-12';
實驗結果
SCORE_RANK_TABLE1
ID | NAME | SCORE | CLASS_NUM | ROW_NUMBERS | DENSE_RANKS | RANKS | pt_dtpt_dt |
---|---|---|---|---|---|---|---|
6 | 小郭 | 99 | 1班 | 1 | 1 | 1 | 2019-12-12 |
4 | 小胖 | 91 | 1班 | 2 | 2 | 2 | 2019-12-12 |
3 | 小軍 | 90 | 1班 | 3 | 3 | 3 | 2019-12-12 |
2 | 小紅 | 90 | 1班 | 4 | 3 | 3 | 2019-12-12 |
1 | 小明 | 89 | 1班 | 5 | 4 | 5 | 2019-12-12 |
5 | 小李 | 87 | 1班 | 6 | 5 | 6 | 2019-12-12 |
如上表所示,1班的小軍和小紅分數均為90,當我們使用ROW_NUMBERS()
進行排序時,他們的排名不會並列,而是分別有一個排名。
當我們使用DENSE_RANK()
進行排序時,他們的排名會並列,且後續記錄的排名會以當前並列排名為基礎+1,即不會跳過被佔用的位置。
當我們使用RANK()
進行排名時,他們的排名會並列,且後續記錄的排名會跳過被佔用的排名數,而不會順延下去。
總結
在實際開發過程中,可根據場景的需要去選擇具體的排序函式。一個較為常見的場景是根據某個欄位partition by
之後在該範圍內order by
進行排序,然後取首條記錄,這時候row_number()
基本可以滿足需求。
除此之外,排序函式均較耗效能,特別是如果對大資料量進行全域性排序時,一定要考慮效能問題,非必要情況下,避免對大資料量進行全域性排序。