本文主要使用實例對Hive內建的一些聚合函數、分析函數以及采樣函數進行比較詳細的講解。
一、基本聚合函數
數據聚合是按照特定條件將數據整合並表達出來,以總結出更多的組信息。Hive包含內建的一些基本聚合函數,如MAX, MIN, AVG等等,同時也通過GROUPING SETS, ROLLUP, CUBE等函數支持更高級的聚合。Hive基本內建聚合函數通常與GROUP BY連用,默認情況下是對整個表進行操作。在使用GROUP BY時,除聚合函數外其他已選擇列必須包含在GROUP BY子句中。
例:計算employee表中數據總條數
hive>
SELECT COUNT(*) FROM employee;
例:計算employee表中數據總條數,sex_age必須包含在GROUP BY的子句中,否則報錯!
hive>
SELECT sex_age, count(*) AS row_cnt FROM employee GROUP BY sex_age;
那么有一個問題,如果我需要選擇一行,但此時我不想對其進行GROUP BY那應該怎么辦呢?這里有兩個方法,一個是后面要講到的使用分析函數,另一個就是使用COLLECT_SET函數,該函數將返回一個包含被GROUP BY排除的列的副本集合。
例:使用COLLECT_SET,其中的列不用進行GROUP BY
hive>
SELECT sex_age, count(*) AS row_cnt FROM employee GROUP BY sex_age;
注:聚合函數在同一個語句中可以組合使用,但是不能嵌套使用,即不能在一個聚合函數中套用另一個聚合函數!
例:組合使用AVG和COUNT
hive>
SELECT sex_age.sex, AVG(sex_age.age) AS avg_age, count(*) AS row_cnt FROM employee GROUP BY sex_age.sex;
例:聚合函數與CASE WHEN組合使用
hive>
SELECT SUM(CASE WHEN sex_age.sex='Male' THEN sex_age.age ELSE 0 END)/COUNT(CASE WHEN sex_age.sex='Male' THEN 1 ELSE NULL END) AS male_age_avg FROM employee;
例:聚合函數與COALESCE和IF組合使用。COALESCE(arg1, arg2, arg3...):遇到非null參數即返回改值
hive>
SELECT SUM(COALESCE(sex_age.age, 0)) AS age_sum, SUM(IF(sex_age.sex='Female',sex_age.age,0)) AS female_age_sum FROM employee;
例:嵌套聚合函數將會報錯
hive>
SELECT AVG(COUNT(*)) AS row_cnt FROM employee;
例:聚合函數與DISTINCT關鍵詞組合使用。DISTINCT: 去重
hive>
SELECT COUNT(DISTINCT sex_age.sex) AS sex_uni_cnt, COUNT(DISTINCT name) AS name_uni_cnt FROM employee;
注:如果COUNT和DISTINCT連用,Hive將忽略對reducer個數的設置(如:set mapred.reduce.tasks=20;), 僅會有一個reducer!此時reduce將成為瓶頸,這時我們可以使用子查詢的方式解決該問題。
----------------- 對比 ----------------------
例:瓶頸問題
hive>
SELECT COUNT(DISTINCT sex_age.sex) AS sex_uni_cnt FROM employee;
例:子查詢解決COUNT, DISTINCT瓶頸
hive>
SELECT COUNT(*) AS sex_uni_cnt FROM (SELECT DISTINCT sex_age.sex FROM employee) a;
注:子句必須有一個別名,否則會解析語句失敗。
在Hive的聚合中,如果某個聚合列的值中有null,則包含該null的行將在聚合時被忽略。為了避免這種情況,可以使用COALESCE來將null替換為一個默認值。
例:
hive>
CREATE TABLE t AS SELECT * FROM (SELECT employee_id-99 AS val1, (employee_id-98) AS val2 FROM employee_hr WHERE employee_id<=101 UNION ALL SELECT null val1, 2 AS val2 FROM employee_hr WHERE employee_id=100) a;
例:
hive>
SELECT * FROM t;
例:val1=null將被忽略,但該行對應的其他非null值繼續被聚合!
hive>
SELECT SUM(val1), SUM(val1 + val2) FROM t;
例:將值為null的使用COALESCE替換為0
hive>
SELECT SUM(COALESCE(val1, 0)), SUM(COALESCE(val1, 0)+val2) FROM t;
可以設置hive.map.aggr屬性來控制map階段的聚合,默認是false。如果設置為true,則將在map任務時進行first-level聚合,這將使得map有更好的性能,但會消耗更多內存。
二、高級聚合
高級聚合主要有以下幾種情況:
1. GROUPING SETS
該關鍵字可以實現對同一個數據集的多重GROUP BY操作。事實上GROUPING SETS是多個GROUP BY進行UNION ALL操作的簡單表達,它僅使用一個stage完成這些操作。GROUPING SETS的子句中如果包換()數據集,則表示整體聚合。
例:
hive>
SELECT name, work_place[0] AS main_place,
count(employee_id) AS emp_id_cnt
FROM employee_id
GROUP BY name, work_place[0]
GROUPING SETS((name, work_place[0]), name, work_place[0], ());
||
SELECT name, work_place[0] AS main_place,
count(employee_id) AS emp_id_cnt
FROM employee_id
GROUP BY name, work_place[0]
UNION ALL
SELECT name, NULL AS main_place, count(employee_id) AS emp_id_cnt
FROM employee_id
GROUP BY name
UNION ALL
SELECT NULL AS name, work_place[0] AS main_place,
count(employee_id) AS emp_id_cnt
FROM employee_id
GROUP BY work_place[0]
UNION ALL
SELECT NULL AS name, NULL AS main_place,
count(employee_id) AS emp_id_cnt
FROM employee_id;
然而GROUPING SETS目前還有未解決的問題,參考HIVE-6950https://issues.apache.org/jira/browse/HIVE-6950
例:
hive>
SELECT sex_age.sex, sex_age.age,
count(name) AS name_cnt
FROM employee
GROUP BY sex_age.sex, sex_age.age
GROUPING SETS((sex_age.sex, sex_age.age));
2. ROLLUP和CUBE
這兩個關鍵字都是GROUP BY的高級實現。
對比於規定了n層聚合的GROUPING SETS,ROLLUP會創建n+1層聚合,在此n表示分組列的個數。
GROUP BY a, b, c WITH ROLLUP 等價於 GROUP BY a,b,c GROUPING SETS ((a,b,c),(a,b),(a),())
CUBE將會對分組列進行所有可能的組合聚合。如果為CUBE指定了n列,則將返回2^n個聚合組合。
GROUP BY a, b, c WITH ROLLUP 等價於 GROUP BY a,b,c GROUPING SETS ((a,b,c),(a,b),(b,c),(a,c),(a),(b),(c),())
三、聚合條件--HAVING
從Hive0.7.0開始HAVING被添加到Hive作為GROUP BY結果集的條件過濾。HAVING可以作為子句的替代。
例:
hive>
SELECT sex_age.age FROM employee GROUP BY sex_age.age HAVING count(*)<=1;
例:作用同上
hive>
SELECT a.age FROM (SELECT COUNT(*) AS cnt, sex_age.age FROM employee GROUP BY sex_age.age) a WHERE a.cnt<=1;
四、解析函數
解析函數是從Hive0.11.0開始被支持,用於掃描多行輸入來計算輸出值。常與OVER, PARTITION BY, ORDER BY等連用。由於解析函數的用法相對復雜,在此不作講解,有興趣的可以上網搜索相關文章進行學習。
五、采樣
當數據集非常大的時候,我們需要找一個子集來加快數據分析。此時我們需要數據采集工具以獲得需要的子集。在此可以使用三種方式獲得采樣數據:random sampling, bucket sampling, block sampling.
1. Random sampling
使用RAND()函數和LIMIT關鍵字來獲取樣例數據。使用DISTRIBUTE和SORT關鍵字來保證數據是隨機分散到mapper和reducer的。ORDER BY RAND()語句可以獲得同樣的效果,但是性能沒這么高。
--Syntax:
SELECT * FROM <Table_Name> DISTRIBUTE BY RAND() SORT BY RAND() LIMIT <N rows to sample>;
2. Bucket table sampling
該方式是最佳化采樣bucket表。RAND()函數也可以用來采樣整行。如果采樣列同時使用了CLUSTERED BY,使用TABLESAMPLE語句會更有效率。
--Syntax:
SELECT * FROM <Table_Name> TABLESAMPLE(BUCKET <specified bucket number to sample> OUT OF <total number of buckets> ON [colname|RAND()]) table_alias;
例:
hive>
CREATE TABLE employee_id_buckets (
name string,
employee_id int,
work_place ARRAY<string>,
sex_age STRUCT<sex:string,age:int>,
skills_score MAP<string,int>,
depart_title MAP<string,ARRAY<string >>
)
CLUSTERED BY (employee_id) INTO 2 BUCKETS
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '|'
COLLECTION ITEMS TERMINATED BY ','
MAP KEYS TERMINATED BY ':';
INSERT OVERWRITE TABLE employee_id_buckets SELECT * FROM employee_id;
SELECT name FROM employee_id_buckets TABLESAMPLE(BUCKET 1 OUT OF 2 ON rand()) a;
3. Block sampling
該方式允許Hive隨機抽取N行數據,數據總量的百分比(n百分比)或N字節的數據。
--Syntax:
SELECT * FROM <Table_Name> TABLESAMPLE(N PERCENT|ByteLengthLiteral|N ROWS) s;
--ByteLengggthLiteral:
--(Digit)+ ('b' | 'B' | 'k' | 'K' | 'm' | 'M' | 'g' | 'G')
例:按行抽樣
hive>
SELECT name FROM employee_id_buckets TABLESAMPLE(4 ROWS) a;
例:按數據量百分比抽樣
hive>
SELECT name FROM employee_id_buckets TABLESAMPLE(10 PERCENT) a;
注:此方法有待考證,在Hive0.11.0中將所有25條數據全取出來了,在Hive0.13.0中取出了其中的12條,但是都不符合要求!!
例:按數據大小采樣
hive>
SELECT name FROM employee_id_buckets TABLESAMPLE(1M) a;
總結
聚合和抽樣,特別是聚合函數,在大數據處理過程中是處理數據的主要方法。通過自由的條件限制以及聚合函數組合,基本能完成任意要求的數據處理或分組。本文僅僅是針對Hive進行了部分比較細致的講解,關於像解析函數之類的使用比較復雜一點的處理方式需要進行更深一步的了解和運用。希望本文能提供到一定的幫助!本文來自於互聯網