訂閱
糾錯
加入自媒體

一文詳解Hive知識體系

2021-08-19 14:54
園陌
關注


lateral view用于和split、explode等UDTF一起使用的,能將一行數據拆分成多行數據,在此基礎上可以對拆分的數據進行聚合,lateral view首先為原始表的每行調用UDTF,UDTF會把一行拆分成一行或者多行,lateral view在把結果組合,產生一個支持別名表的虛擬表。

其中explode還可以用于將hive一列中復雜的array或者map結構拆分成多行

需求:現在有數據格式如下

zhangsan child1,child2,child3,child4 k1:v1,k2:v2
lisi child5,child6,child7,child8 k3:v3,k4:v4

字段之間使用 分割,需求將所有的child進行拆開成為一列

+----------+--+
| mychild  |
+----------+--+
| child1   |
| child2   |
| child3   |
| child4   |
| child5   |
| child6   |
| child7   |
| child8   |
+----------+--+

將map的key和value也進行拆開,成為如下結果

+-----------+-------------+--+
| mymapkey  | mymapvalue  |
+-----------+-------------+--+
| k1        | v1          |
| k2        | v2          |
| k3        | v3          |
| k4        | v4          |
+-----------+-------------+--+
創建hive數據庫創建hive數據庫
hive (default)> create database hive_explode;
hive (default)> use hive_explode;
創建hive表,然后使用explode拆分map和arrayhive (hive_explode)> create  table t3(name string,children array

需求: 需求:現在有一些數據格式如下:

a:shandong,b:beijing,c:hebei|1,2,3,4,5,6,7,8,9|[{"source":"7fresh","monthSales":4900,"userCount":1900,"score":"9.9"},{"source":"jd","monthSales":2090,"userCount":78981,"score":"9.8"},{"source":"jdmart","monthSales":6987,"userCount":1600,"score":"9.0"}]

其中字段與字段之間的分隔符是 |

我們要解析得到所有的monthSales對應的值為以下這一列(行轉列)

4900

2090

6987


創建hive表hive (hive_explode)> create table explode_lateral_view
                  > (`area` string,
                  > `goods_id` string,
                  > `sale_info` string)
                  > ROW FORMAT DELIMITED
                  > FIELDS TERMINATED BY '|'
                  > STORED AS textfile;
準備數據并加載數據準備數據如下
cd /export/servers/hivedatas
vim explode_json
a:shandong,b:beijing,c:hebei|1,2,3,4,5,6,7,8,9|[{"source":"7fresh","monthSales":4900,"userCount":1900,"score":"9.9"},{"source":"jd","monthSales":2090,"userCount":78981,"score":"9.8"},{"source":"jdmart","monthSales":6987,"userCount":1600,"score":"9.0"}]
加載數據到hive表當中去
hive (hive_explode)> load data local inpath '/export/servers/hivedatas/explode_json' overwrite into table explode_lateral_view;
使用explode拆分Arrayhive (hive_explode)> select explode(split(goods_id,',')) as goods_id from explode_lateral_view;
使用explode拆解Maphive (hive_explode)> select explode(split(area,',')) as area from explode_lateral_view;
拆解json字段hive (hive_explode)> select explode(split(regexp_replace(regexp_replace(sale_info,'\[\{',''),'}]',''),'},\{')) as  sale_info from explode_lateral_view;
然后我們想用get_json_object來獲取key為monthSales的數據:
hive (hive_explode)> select get_json_object(explode(split(regexp_replace(regexp_replace(sale_info,'\[\{',''),'}]',''),'},\{')),'$.monthSales') as  sale_info from explode_lateral_view;
然后掛了FAILED: SemanticException [Error 10081]: UDTF's are not supported outside the SELECT clause, nor nested in expressions
UDTF explode不能寫在別的函數內
如果你這么寫,想查兩個字段,select explode(split(area,',')) as area,good_id from explode_lateral_view;
會報錯FAILED: SemanticException 1:40 Only a single expression in the SELECT clause is supported with UDTF's. Error encountered near token 'good_id'
使用UDTF的時候,只支持一個字段,這時候就需要LATERAL VIEW出場了
配合LATERAL  VIEW使用

配合lateral view查詢多個字段

hive (hive_explode)> select goods_id2,sale_info from explode_lateral_view LATERAL VIEW explode(split(goods_id,','))goods as goods_id2;
其中LATERAL VIEW explode(split(goods_id,','))goods相當于一個虛擬表,與原表explode_lateral_view笛卡爾積關聯

也可以多重使用

hive (hive_explode)> select goods_id2,sale_info,area2
                   from explode_lateral_view
                   LATERAL VIEW explode(split(goods_id,','))goods as goods_id2
                   LATERAL VIEW explode(split(area,','))area as area2;也是三個表笛卡爾積的結果

最終,我們可以通過下面的句子,把這個json格式的一行數據,完全轉換成二維表的方式展現

hive (hive_explode)> select get_json_object(concat('{',sale_info_1,'}'),'$.source') as source,get_json_object(concat('{',sale_info_1,'}'),'$.monthSales') as monthSales,get_json_object(concat('{',sale_info_1,'}'),'$.userCount') as monthSales,get_json_object(concat('{',sale_info_1,'}'),'$.score') as monthSales from explode_lateral_view LATERAL VIEW explode(split(regexp_replace(regexp_replace(sale_info,'\[\{',''),'}]',''),'},\{'))sale_info as sale_info_1;

總結:

Lateral View通常和UDTF一起出現,為了解決UDTF不允許在select字段的問題。Multiple Lateral View可以實現類似笛卡爾乘積。Outer關鍵字可以把不輸出的UDTF的空結果,輸出成NULL,防止丟失數據。

行轉列

相關參數說明:

CONCAT(string A/col, string B/col…):返回輸入字符串連接后的結果,支持任意個輸入字符串;

CONCAT_WS(separator, str1, str2,...):它是一個特殊形式的 CONCAT()。第一個參數剩余參數間的分隔符。分隔符可以是與剩余參數一樣的字符串。如果分隔符是 NULL,返回值也將為 NULL。這個函數會跳過分隔符參數后的任何 NULL 和空字符串。分隔符將被加到被連接的字符串之間;

COLLECT_SET(col):函數只接受基本數據類型,它的主要作用是將某字段的值進行去重匯總,產生array類型字段。

數據準備:

nameconstellationblood_type孫悟空白羊座A老王射手座A宋宋白羊座B豬八戒白羊座A鳳姐射手座A

需求: 把星座和血型一樣的人歸類到一起。結果如下:

射手座,A            老王|鳳姐
白羊座,A            孫悟空|豬八戒
白羊座,B            宋宋

實現步驟:


創建本地constellation.txt,導入數據node03服務器執行以下命令創建文件,注意數據使用 進行分割
cd /export/servers/hivedatas
vim constellation.txt
數據如下:
孫悟空 白羊座 A
老王 射手座 A
宋宋 白羊座 B      
豬八戒 白羊座 A
鳳姐 射手座 A
創建hive表并導入數據創建hive表并加載數據
hive (hive_explode)> create table person_info(
                   name string,
                   constellation string,
                   blood_type string)
                   row format delimited fields terminated by " ";
                   
加載數據
hive (hive_explode)> load data local inpath '/export/servers/hivedatas/constellation.txt' into table person_info;
按需求查詢數據hive (hive_explode)> select
                       t1.base,
                       concat_ws('|', collect_set(t1.name)) name
                   from
                       (select
                           name,
                           concat(constellation, "," , blood_type) base
                       from
                           person_info) t1
                   group by
                       t1.base;
列轉行

所需函數:

EXPLODE(col):將hive一列中復雜的array或者map結構拆分成多行。

LATERAL VIEW

用法:LATERAL VIEW udtf(expression) tableAlias AS columnAlias

解釋:用于和split, explode等UDTF一起使用,它能夠將一列數據拆成多行數據,在此基礎上可以對拆分后的數據進行聚合。

數據準備:

cd /export/servers/hivedatas
vim movie.txt
文件內容如下:  數據字段之間使用 進行分割
《疑犯追蹤》 懸疑,動作,科幻,劇情
《Lie to me》 懸疑,警匪,動作,心理,劇情
《戰狼2》 戰爭,動作,災難

需求: 將電影分類中的數組數據展開。結果如下:

《疑犯追蹤》 懸疑
《疑犯追蹤》 動作
《疑犯追蹤》 科幻
《疑犯追蹤》 劇情
《Lie to me》 懸疑
《Lie to me》 警匪
《Lie to me》 動作
《Lie to me》 心理
《Lie to me》 劇情
《戰狼2》 戰爭
《戰狼2》 動作
《戰狼2》 災難

實現步驟:


創建hive表create table movie_info(
   movie string,
   category array

reflect函數可以支持在sql中調用java中的自帶函數,秒殺一切udf函數。

需求1: 使用java.lang.Math當中的Max求兩列中最大值

實現步驟:


創建hive表create table test_udf(col1 int,col2 int) row format delimited fields terminated by ',';
準備數據并加載數據cd /export/servers/hivedatas
vim test_udf
文件內容如下:
1,2
4,3
6,4
7,5
5,6
加載數據hive (hive_explode)> load data local inpath '/export/servers/hivedatas/test_udf' overwrite into table test_udf;
使用java.lang.Math當中的Max求兩列當中的最大值hive (hive_explode)> select reflect("java.lang.Math","max",col1,col2) from test_udf;

需求2: 文件中不同的記錄來執行不同的java的內置函數

實現步驟:


創建hive表hive (hive_explode)> create table test_udf2(class_name string,method_name string,col1 int , col2 int) row format delimited fields terminated by ',';
準備數據cd /export/servers/hivedatas
vim test_udf2
文件內容如下:
java.lang.Math,min,1,2
java.lang.Math,max,2,3
加載數據hive (hive_explode)> load data local inpath '/export/servers/hivedatas/test_udf2' overwrite into table test_udf2;
執行查詢hive (hive_explode)> select reflect(class_name,method_name,col1,col2) from test_udf2;

需求3: 判斷是否為數字

實現方式:

使用apache commons中的函數,commons下的jar已經包含在hadoop的classpath中,所以可以直接使用。

select reflect("org.apache.commons.lang.math.NumberUtils","isNumber","123")
窗口函數與分析函數

在sql中有一類函數叫做聚合函數,例如sum()、avg()、max()等等,這類函數可以將多行數據按照規則聚集為一行,一般來講聚集后的行數是要少于聚集前的行數的。但是有時我們想要既顯示聚集前的數據,又要顯示聚集后的數據,這時我們便引入了窗口函數。窗口函數又叫OLAP函數/分析函數,窗口函數兼具分組和排序功能。

窗口函數最重要的關鍵字是 partition byorder by。

具體語法如下:over (partition by xxx order by xxx)

sum、avg、min、max

準備數據

建表語句:
create table test_t1(
cookieid string,
createtime string,   --day
pv int
) row format delimited
fields terminated by ',';
加載數據:
load data local inpath '/root/hivedata/test_t1.dat' into table test_t1;
cookie1,2020-04-10,1
cookie1,2020-04-11,5
cookie1,2020-04-12,7
cookie1,2020-04-13,3
cookie1,2020-04-14,2
cookie1,2020-04-15,4
cookie1,2020-04-16,4
開啟智能本地模式
SET hive.exec.mode.local.auto=true;

SUM函數和窗口函數的配合使用:結果和ORDER BY相關,默認為升序。

select cookieid,createtime,pv,
sum(pv) over(partition by cookieid order by createtime) as pv1
from test_t1;
select cookieid,createtime,pv,
sum(pv) over(partition by cookieid order by createtime rows between unbounded preceding and current row) as pv2
from test_t1;
select cookieid,createtime,pv,
sum(pv) over(partition by cookieid) as pv3
from test_t1;
select cookieid,createtime,pv,
sum(pv) over(partition by cookieid order by createtime rows between 3 preceding and current row) as pv4
from test_t1;
select cookieid,createtime,pv,
sum(pv) over(partition by cookieid order by createtime rows between 3 preceding and 1 following) as pv5
from test_t1;
select cookieid,createtime,pv,
sum(pv) over(partition by cookieid order by createtime rows between current row and unbounded following) as pv6
from test_t1;
pv1: 分組內從起點到當前行的pv累積,如,11號的pv1=10號的pv+11號的pv, 12號=10號+11號+12號
pv2: 同pv1
pv3: 分組內(cookie1)所有的pv累加
pv4: 分組內當前行+往前3行,如,11號=10號+11號, 12號=10號+11號+12號,
                       13號=10號+11號+12號+13號, 14號=11號+12號+13號+14號
pv5: 分組內當前行+往前3行+往后1行,如,14號=11號+12號+13號+14號+15號=5+7+3+2+4=21
pv6: 分組內當前行+往后所有行,如,13號=13號+14號+15號+16號=3+2+4+4=13,
       14號=14號+15號+16號=2+4+4=10

如果不指定rows between,默認為從起點到當前行;

如果不指定order by,則將分組內所有值累加;

關鍵是理解rows between含義,也叫做window子句:

preceding:往前

following:往后

current row:當前行

unbounded:起點

unbounded preceding 表示從前面的起點

unbounded following:表示到后面的終點

AVG,MIN,MAX,和SUM用法一樣。

row_number、rank、dense_rank、ntile

準備數據

cookie1,2020-04-10,1
cookie1,2020-04-11,5
cookie1,2020-04-12,7
cookie1,2020-04-13,3
cookie1,2020-04-14,2
cookie1,2020-04-15,4
cookie1,2020-04-16,4
cookie2,2020-04-10,2
cookie2,2020-04-11,3
cookie2,2020-04-12,5
cookie2,2020-04-13,6
cookie2,2020-04-14,3
cookie2,2020-04-15,9
cookie2,2020-04-16,7

CREATE TABLE test_t2 (
cookieid string,
createtime string,   --day
pv INT
) ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
stored as textfile;
 
加載數據:
load data local inpath '/root/hivedata/test_t2.dat' into table test_t2;

ROW_NUMBER()使用

ROW_NUMBER()從1開始,按照順序,生成分組內記錄的序列。

SELECT
cookieid,
createtime,
pv,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn
FROM test_t2;

RANK 和 DENSE_RANK使用

RANK() 生成數據項在分組中的排名,排名相等會在名次中留下空位 。

DENSE_RANK()生成數據項在分組中的排名,排名相等會在名次中不會留下空位。

SELECT
cookieid,
createtime,
pv,
RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn1,
DENSE_RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn2,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY pv DESC) AS rn3
FROM test_t2
WHERE cookieid = 'cookie1';

NTILE

有時會有這樣的需求:如果數據排序后分為三部分,業務人員只關心其中的一部分,如何將這中間的三分之一數據拿出來呢?NTILE函數即可以滿足。

ntile可以看成是:把有序的數據集合平均分配到指定的數量(num)個桶中, 將桶號分配給每一行。如果不能平均分配,則優先分配較小編號的桶,并且各個桶中能放的行數最多相差1。

然后可以根據桶號,選取前或后 n分之幾的數據。數據會完整展示出來,只是給相應的數據打標簽;具體要取幾分之幾的數據,需要再嵌套一層根據標簽取出。

SELECT
cookieid,
createtime,
pv,
NTILE(2) OVER(PARTITION BY cookieid ORDER BY createtime) AS rn1,
NTILE(3) OVER(PARTITION BY cookieid ORDER BY createtime) AS rn2,
NTILE(4) OVER(ORDER BY createtime) AS rn3
FROM test_t2
ORDER BY cookieid,createtime;
其他一些窗口函數lag,lead,first_value,last_valueLAG
LAG(col,n,DEFAULT) 用于統計窗口內往上第n行值第一個參數為列名,第二個參數為往上第n行(可選,默認為1),第三個參數為默認值(當往上第n行為NULL時候,取默認值,如不指定,則為NULL)SELECT cookieid,
createtime,
url,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
LAG(createtime,1,'1970-01-01 00:00:00') OVER(PARTITION BY cookieid ORDER BY createtime) AS last_1_time,
LAG(createtime,2) OVER(PARTITION BY cookieid ORDER BY createtime) AS last_2_time
FROM test_t4;
last_1_time: 指定了往上第1行的值,default為'1970-01-01 00:00:00'  
                cookie1第一行,往上1行為NULL,因此取默認值 1970-01-01 00:00:00
                cookie1第三行,往上1行值為第二行值,2015-04-10 10:00:02
                cookie1第六行,往上1行值為第五行值,2015-04-10 10:50:01
last_2_time: 指定了往上第2行的值,為指定默認值
        cookie1第一行,往上2行為NULL
        cookie1第二行,往上2行為NULL
        cookie1第四行,往上2行為第二行值,2015-04-10 10:00:02
        cookie1第七行,往上2行為第五行值,2015-04-10 10:50:01
LEAD

與LAG相反LEAD(col,n,DEFAULT) 用于統計窗口內往下第n行值第一個參數為列名,第二個參數為往下第n行(可選,默認為1),第三個參數為默認值(當往下第n行為NULL時候,取默認值,如不指定,則為NULL)

SELECT cookieid,
createtime,
url,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
LEAD(createtime,1,'1970-01-01 00:00:00') OVER(PARTITION BY cookieid ORDER BY createtime) AS next_1_time,
LEAD(createtime,2) OVER(PARTITION BY cookieid ORDER BY createtime) AS next_2_time
FROM test_t4;

FIRST_VALUE

取分組內排序后,截止到當前行,第一個值

SELECT cookieid,
createtime,
url,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
FIRST_VALUE(url) OVER(PARTITION BY cookieid ORDER BY createtime) AS first1
FROM test_t4;
LAST_VALUE

取分組內排序后,截止到當前行,最后一個值

SELECT cookieid,
createtime,
url,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
LAST_VALUE(url) OVER(PARTITION BY cookieid ORDER BY createtime) AS last1
FROM test_t4;

如果想要取分組內排序后最后一個值,則需要變通一下:

SELECT cookieid,
createtime,
url,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
LAST_VALUE(url) OVER(PARTITION BY cookieid ORDER BY createtime) AS last1,
FIRST_VALUE(url) OVER(PARTITION BY cookieid ORDER BY createtime DESC) AS last2
FROM test_t4
ORDER BY cookieid,createtime;

特別注意order  by

如果不指定ORDER BY,則進行排序混亂,會出現錯誤的結果

SELECT cookieid,
createtime,
url,
FIRST_VALUE(url) OVER(PARTITION BY cookieid) AS first2  
FROM test_t4;
cume_dist,percent_rank

這兩個序列分析函數不是很常用,注意:序列函數不支持WINDOW子句

數據準備d1,user1,1000
d1,user2,2000
d1,user3,3000
d2,user4,4000
d2,user5,5000

CREATE EXTERNAL TABLE test_t3 (
dept STRING,
userid string,
sal INT
) ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
stored as textfile;
加載數據:
load data local inpath '/root/hivedata/test_t3.dat' into table test_t3;

CUME_DIST  和order byd的排序順序有關系

CUME_DIST 小于等于當前值的行數/分組內總行數  order 默認順序 正序 升序比如,統計小于等于當前薪水的人數,所占總人數的比例

SELECT
dept,
userid,
sal,
CUME_DIST() OVER(ORDER BY sal) AS rn1,
CUME_DIST() OVER(PARTITION BY dept ORDER BY sal) AS rn2
FROM test_t3;

rn1: 沒有partition,所有數據均為1組,總行數為5,
     第一行:小于等于1000的行數為1,因此,1/5=0.2
     第三行:小于等于3000的行數為3,因此,3/5=0.6
rn2: 按照部門分組,dpet=d1的行數為3,
     第二行:小于等于2000的行數為2,因此,2/3=0.6666666666666666

PERCENT_RANK

PERCENT_RANK 分組內當前行的RANK值-1/分組內總行數-1

經調研 該函數顯示現實意義不明朗 有待于繼續考證

 SELECT
 dept,
 userid,
 sal,
 PERCENT_RANK() OVER(ORDER BY sal) AS rn1,   --分組內
 RANK() OVER(ORDER BY sal) AS rn11,          --分組內RANK值
 SUM(1) OVER(PARTITION BY NULL) AS rn12,     --分組內總行數
 PERCENT_RANK() OVER(PARTITION BY dept ORDER BY sal) AS rn2
 FROM test_t3;
 
 rn1: rn1 = (rn11-1) / (rn12-1)
     第一行,(1-1)/(5-1)=0/4=0
     第二行,(2-1)/(5-1)=1/4=0.25
     第四行,(4-1)/(5-1)=3/4=0.75
 rn2: 按照dept分組,
      dept=d1的總行數為3
      第一行,(1-1)/(3-1)=0
      第三行,(3-1)/(3-1)=1
grouping sets,grouping__id,cube,rollup

這幾個分析函數通常用于OLAP中,不能累加,而且需要根據不同維度上鉆和下鉆的指標統計,比如,分小時、天、月的UV數。

數據準備2020-03,2020-03-10,cookie1
2020-03,2020-03-10,cookie5
2020-03,2020-03-12,cookie7
2020-04,2020-04-12,cookie3
2020-04,2020-04-13,cookie2
2020-04,2020-04-13,cookie4
2020-04,2020-04-16,cookie4
2020-03,2020-03-10,cookie2
2020-03,2020-03-10,cookie3
2020-04,2020-04-12,cookie5
2020-04,2020-04-13,cookie6
2020-04,2020-04-15,cookie3
2020-04,2020-04-15,cookie2
2020-04,2020-04-16,cookie1

CREATE TABLE test_t5 (
month STRING,
day STRING,
cookieid STRING
) ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
stored as textfile;
加載數據:
load data local inpath '/root/hivedata/test_t5.dat' into table test_t5;
GROUPING SETS

grouping sets是一種將多個group by 邏輯寫在一個sql語句中的便利寫法。

等價于將不同維度的GROUP BY結果集進行UNION ALL。

GROUPING__ID,表示結果屬于哪一個分組集合。

SELECT
month,
day,
COUNT(DISTINCT cookieid) AS uv,
GROUPING__ID
FROM test_t5
GROUP BY month,day
GROUPING SETS (month,day)
ORDER BY GROUPING__ID;
grouping_id表示這一組結果屬于哪個分組集合,
根據grouping sets中的分組條件month,day,1是代表month,2是代表day
等價于
SELECT month,NULL,COUNT(DISTINCT cookieid) AS uv,1 AS GROUPING__ID FROM test_t5 GROUP BY month UNION ALL
SELECT NULL as month,day,COUNT(DISTINCT cookieid) AS uv,2 AS GROUPING__ID FROM test_t5 GROUP BY day;

再如:

SELECT
month,
day,
COUNT(DISTINCT cookieid) AS uv,
GROUPING__ID
FROM test_t5
GROUP BY month,day
GROUPING SETS (month,day,(month,day))
ORDER BY GROUPING__ID;
等價于
SELECT month,NULL,COUNT(DISTINCT cookieid) AS uv,1 AS GROUPING__ID FROM test_t5 GROUP BY month
UNION ALL
SELECT NULL,day,COUNT(DISTINCT cookieid) AS uv,2 AS GROUPING__ID FROM test_t5 GROUP BY day
UNION ALL
SELECT month,day,COUNT(DISTINCT cookieid) AS uv,3 AS GROUPING__ID FROM test_t5 GROUP BY month,day;
CUBE

根據GROUP BY的維度的所有組合進行聚合。

SELECT
month,
day,
COUNT(DISTINCT cookieid) AS uv,
GROUPING__ID
FROM test_t5
GROUP BY month,day
WITH CUBE
ORDER BY GROUPING__ID;
等價于
SELECT NULL,NULL,COUNT(DISTINCT cookieid) AS uv,0 AS GROUPING__ID FROM test_t5
UNION ALL
SELECT month,NULL,COUNT(DISTINCT cookieid) AS uv,1 AS GROUPING__ID FROM test_t5 GROUP BY month
UNION ALL
SELECT NULL,day,COUNT(DISTINCT cookieid) AS uv,2 AS GROUPING__ID FROM test_t5 GROUP BY day
UNION ALL
SELECT month,day,COUNT(DISTINCT cookieid) AS uv,3 AS GROUPING__ID FROM test_t5 GROUP BY month,day;
ROLLUP

是CUBE的子集,以最左側的維度為主,從該維度進行層級聚合。

比如,以month維度進行層級聚合:
SELECT
month,
day,
COUNT(DISTINCT cookieid) AS uv,
GROUPING__ID  
FROM test_t5
GROUP BY month,day
WITH ROLLUP
ORDER BY GROUPING__ID;
--把month和day調換順序,則以day維度進行層級聚合:

SELECT
day,
month,
COUNT(DISTINCT cookieid) AS uv,
GROUPING__ID  
FROM test_t5
GROUP BY day,month
WITH ROLLUP
ORDER BY GROUPING__ID;
(這里,根據天和月進行聚合,和根據天聚合結果一樣,因為有父子關系,如果是其他維度組合的話,就會不一樣)
七、Hive執行計劃

Hive SQL的執行計劃描述SQL實際執行的整體輪廓,通過執行計劃能了解SQL程序在轉換成相應計算引擎的執行邏輯,掌握了執行邏輯也就能更好地把握程序出現的瓶頸點,從而能夠實現更有針對性的優化。此外還能幫助開發者識別看似等價的SQL其實是不等價的,看似不等價的SQL其實是等價的SQL。可以說執行計劃是打開SQL優化大門的一把鑰匙。

要想學SQL執行計劃,就需要學習查看執行計劃的命令:explain,在查詢語句的SQL前面加上關鍵字explain是查看執行計劃的基本方法。

學會explain,能夠給我們工作中使用hive帶來極大的便利!

查看SQL的執行計劃

Hive提供的執行計劃目前可以查看的信息有以下幾種:

explain:查看執行計劃的基本信息;

explain dependency:dependency在explain語句中使用會產生有關計劃中輸入的額外信息。它顯示了輸入的各種屬性;

explain authorization:查看SQL操作相關權限的信息;

explain vectorization:查看SQL的向量化描述信息,顯示為什么未對Map和Reduce進行矢量化。從 Hive 2.3.0 開始支持;

explain analyze:用實際的行數注釋計劃。從 Hive 2.2.0 開始支持;

explain cbo:輸出由Calcite優化器生成的計劃。CBO 從 Hive 4.0.0 版本開始支持;

explain locks:這對于了解系統將獲得哪些鎖以運行指定的查詢很有用。LOCKS 從 Hive 3.2.0 開始支持;

explain ast:輸出查詢的抽象語法樹。AST 在 Hive 2.1.0 版本刪除了,存在bug,轉儲AST可能會導致OOM錯誤,將在4.0.0版本修復;

explain extended:加上 extended 可以輸出有關計劃的額外信息。這通常是物理信息,例如文件名,這些額外信息對我們用處不大;

1.  explain 的用法

Hive提供了explain命令來展示一個查詢的執行計劃,這個執行計劃對于我們了解底層原理,Hive 調優,排查數據傾斜等很有幫助。

使用語法如下:

explain query;

在 hive cli 中輸入以下命令(hive 2.3.7):

explain select sum(id) from test1;

得到結果:

STAGE DEPENDENCIES:
 Stage-1 is a root stage
 Stage-0 depends on stages: Stage-1
STAGE PLANS:
 Stage: Stage-1
   Map Reduce
     Map Operator Tree:
         TableScan
           alias: test1
           Statistics: Num rows: 6 Data size: 75 Basic stats: COMPLETE Column stats: NONE
           Select Operator
             expressions: id (type: int)
             outputColumnNames: id
             Statistics: Num rows: 6 Data size: 75 Basic stats: COMPLETE Column stats: NONE
             Group By Operator
               aggregations: sum(id)
               mode: hash
               outputColumnNames: _col0
               Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE Column stats: NONE
               Reduce Output Operator
                 sort order:
                 Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE Column stats: NONE
                 value expressions: _col0 (type: bigint)
     Reduce Operator Tree:
       Group By Operator
         aggregations: sum(VALUE._col0)
         mode: mergepartial
         outputColumnNames: _col0
         Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE Column stats: NONE
         File Output Operator
           compressed: false
           Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE Column stats: NONE
           table:
               input format: org.apache.hadoop.mapred.SequenceFileInputFormat
               output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat
               serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
 Stage: Stage-0
   Fetch Operator
     limit: -1
     Processor Tree:
       ListSink

看完以上內容有什么感受,是不是感覺都看不懂,不要著急,下面將會詳細講解每個參數,相信你學完下面的內容之后再看 explain 的查詢結果將游刃有余。

一個HIVE查詢被轉換為一個由一個或多個stage組成的序列(有向無環圖DAG)。這些stage可以是MapReduce stage,也可以是負責元數據存儲的stage,也可以是負責文件系統的操作(比如移動和重命名)的stage。

我們將上述結果拆分看,先從最外層開始,包含兩個大的部分:

stage dependencies:各個stage之間的依賴性stage plan:各個stage的執行計劃

先看第一部分 stage dependencies ,包含兩個 stage,Stage-1 是根stage,說明這是開始的stage,Stage-0 依賴 Stage-1,Stage-1執行完成后執行Stage-0。

再看第二部分 stage plan,里面有一個 Map Reduce,一個MR的執行計劃分為兩個部分:

Map Operator Tree:MAP端的執行計劃樹Reduce Operator Tree:Reduce端的執行計劃樹

這兩個執行計劃樹里面包含這條sql語句的 operator:

TableScan:表掃描操作,map端第一個操作肯定是加載表,所以就是表掃描操作,常見的屬性:alias:表名稱Statistics:表統計信息,包含表中數據條數,數據大小等Select Operator:選取操作,常見的屬性 :expressions:需要的字段名稱及字段類型outputColumnNames:輸出的列名稱Statistics:表統計信息,包含表中數據條數,數據大小等Group By Operator:分組聚合操作,常見的屬性:aggregations:顯示聚合函數信息mode:聚合模式,值有 hash:隨機聚合,就是hash partition;partial:局部聚合;final:最終聚合keys:分組的字段,如果沒有分組,則沒有此字段outputColumnNames:聚合之后輸出列名Statistics:表統計信息,包含分組聚合之后的數據條數,數據大小等Reduce Output Operator:輸出到reduce操作,常見屬性:sort order:值為空 不排序;值為 + 正序排序,值為 - 倒序排序;值為 +-  排序的列為兩列,第一列為正序,第二列為倒序Filter Operator:過濾操作,常見的屬性:predicate:過濾條件,如sql語句中的where id>=1,則此處顯示(id >= 1)Map Join Operator:join 操作,常見的屬性:condition map:join方式 ,如Inner Join 0 to 1 Left Outer Join0 to 2keys: join 的條件字段outputColumnNames:join 完成之后輸出的字段Statistics:join 完成之后生成的數據條數,大小等File Output Operator:文件輸出操作,常見的屬性compressed:是否壓縮table:表的信息,包含輸入輸出文件格式化方式,序列化方式等Fetch Operator 客戶端獲取數據操作,常見的屬性:limit,值為 -1 表示不限制條數,其他值為限制的條數2. explain 的使用場景

本節介紹 explain 能夠為我們在生產實踐中帶來哪些便利及解決我們哪些迷惑

案例一:join 語句會過濾 null 的值嗎?

現在,我們在hive cli 輸入以下查詢計劃語句

select a.id,b.user_name from test1 a join test2 b on a.id=b.id;

問:上面這條 join 語句會過濾 id 為 null 的值嗎

執行下面語句:

explain select a.id,b.user_name from test1 a join test2 b on a.id=b.id;

我們來看結果 (為了適應頁面展示,僅截取了部分輸出信息):

TableScan
alias: a
Statistics: Num rows: 6 Data size: 75 Basic stats: COMPLETE Column stats: NONE
Filter Operator
   predicate: id is not null (type: boolean)
   Statistics: Num rows: 6 Data size: 75 Basic stats: COMPLETE Column stats: NONE
   Select Operator
       expressions: id (type: int)
       outputColumnNames: _col0
       Statistics: Num rows: 6 Data size: 75 Basic stats: COMPLETE Column stats: NONE
       HashTable Sink Operator
          keys:
            0 _col0 (type: int)
            1 _col0 (type: int)
...

從上述結果可以看到 predicate: id is not null 這樣一行,說明 join 時會自動過濾掉關聯字段為 null值的情況,但 left join 或 full join 是不會自動過濾null值的,大家可以自行嘗試下。

案例二:group by 分組語句會進行排序嗎?

看下面這條sql

select id,max(user_name) from test1 group by id;

問:group by 分組語句會進行排序嗎

直接來看 explain 之后結果 (為了適應頁面展示,僅截取了部分輸出信息)

TableScan
   alias: test1
   Statistics: Num rows: 9 Data size: 108 Basic stats: COMPLETE Column stats: NONE
   Select Operator
       expressions: id (type: int), user_name (type: string)
       outputColumnNames: id, user_name
       Statistics: Num rows: 9 Data size: 108 Basic stats: COMPLETE Column stats: NONE
       Group By Operator
          aggregations: max(user_name)
          keys: id (type: int)
          mode: hash
          outputColumnNames: _col0, _col1
          Statistics: Num rows: 9 Data size: 108 Basic stats: COMPLETE Column stats: NONE
          Reduce Output Operator
            key expressions: _col0 (type: int)
            sort order: +
            Map-reduce partition columns: _col0 (type: int)
            Statistics: Num rows: 9 Data size: 108 Basic stats: COMPLETE Column stats: NONE
            value expressions: _col1 (type: string)
...

我們看 Group By Operator,里面有 keys: id (type: int) 說明按照 id 進行分組的,再往下看還有 sort order: + ,說明是按照 id 字段進行正序排序的

案例三:哪條sql執行效率高呢?

觀察兩條sql語句

SELECT
a.id,
b.user_name
FROM
test1 a
JOIN test2 b ON a.id = b.id
WHERE
a.id > 2;
SELECT
a.id,
b.user_name
FROM
(SELECT * FROM test1 WHERE id > 2) a
JOIN test2 b ON a.id = b.id;

這兩條sql語句輸出的結果是一樣的,但是哪條sql執行效率高呢?

有人說第一條sql執行效率高,因為第二條sql有子查詢,子查詢會影響性能;

有人說第二條sql執行效率高,因為先過濾之后,在進行join時的條數減少了,所以執行效率就高了。

到底哪條sql效率高呢,我們直接在sql語句前面加上 explain,看下執行計劃不就知道了嘛!

在第一條sql語句前加上 explain,得到如下結果

hive (default)> explain select a.id,b.user_name from test1 a join test2 b on a.id=b.id where a.id >2;
OK
Explain
STAGE DEPENDENCIES:
 Stage-4 is a root stage
 Stage-3 depends on stages: Stage-4
 Stage-0 depends on stages: Stage-3
STAGE PLANS:
 Stage: Stage-4
   Map Reduce Local Work
     Alias -> Map Local Tables:
       $hdt$_0:a
         Fetch Operator
           limit: -1
     Alias -> Map Local Operator Tree:
       $hdt$_0:a
         TableScan
           alias: a
           Statistics: Num rows: 6 Data size: 75 Basic stats: COMPLETE Column stats: NONE
           Filter Operator
             predicate: (id > 2) (type: boolean)
             Statistics: Num rows: 2 Data size: 25 Basic stats: COMPLETE Column stats: NONE
             Select Operator
               expressions: id (type: int)
               outputColumnNames: _col0
               Statistics: Num rows: 2 Data size: 25 Basic stats: COMPLETE Column stats: NONE
               HashTable Sink Operator
                 keys:
                   0 _col0 (type: int)
                   1 _col0 (type: int)
 Stage: Stage-3
   Map Reduce
     Map Operator Tree:
         TableScan
           alias: b
           Statistics: Num rows: 6 Data size: 75 Basic stats: COMPLETE Column stats: NONE
           Filter Operator
             predicate: (id > 2) (type: boolean)
             Statistics: Num rows: 2 Data size: 25 Basic stats: COMPLETE Column stats: NONE
             Select Operator
               expressions: id (type: int), user_name (type: string)
               outputColumnNames: _col0, _col1
               Statistics: Num rows: 2 Data size: 25 Basic stats: COMPLETE Column stats: NONE
               Map Join Operator
                 condition map:
                      Inner Join 0 to 1
                 keys:
                   0 _col0 (type: int)
                   1 _col0 (type: int)
                 outputColumnNames: _col0, _col2
                 Statistics: Num rows: 2 Data size: 27 Basic stats: COMPLETE Column stats: NONE
                 Select Operator
                   expressions: _col0 (type: int), _col2 (type: string)
                   outputColumnNames: _col0, _col1
                   Statistics: Num rows: 2 Data size: 27 Basic stats: COMPLETE Column stats: NONE
                   File Output Operator
                     compressed: false
                     Statistics: Num rows: 2 Data size: 27 Basic stats: COMPLETE Column stats: NONE
                     table:
                         input format: org.apache.hadoop.mapred.SequenceFileInputFormat
                         output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat
                         serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
     Local Work:
       Map Reduce Local Work
 Stage: Stage-0
   Fetch Operator
     limit: -1
     Processor Tree:
       ListSink

在第二條sql語句前加上 explain,得到如下結果

hive (default)> explain select a.id,b.user_name from(select * from  test1 where id>2 ) a join test2 b on a.id=b.id;
OK
Explain
STAGE DEPENDENCIES:
 Stage-4 is a root stage
 Stage-3 depends on stages: Stage-4
 Stage-0 depends on stages: Stage-3
STAGE PLANS:
 Stage: Stage-4
   Map Reduce Local Work
     Alias -> Map Local Tables:
       $hdt$_0:test1
         Fetch Operator
           limit: -1
     Alias -> Map Local Operator Tree:
       $hdt$_0:test1
         TableScan
           alias: test1
           Statistics: Num rows: 6 Data size: 75 Basic stats: COMPLETE Column stats: NONE
           Filter Operator
             predicate: (id > 2) (type: boolean)
             Statistics: Num rows: 2 Data size: 25 Basic stats: COMPLETE Column stats: NONE
             Select Operator
               expressions: id (type: int)
               outputColumnNames: _col0
               Statistics: Num rows: 2 Data size: 25 Basic stats: COMPLETE Column stats: NONE
               HashTable Sink Operator
                 keys:
                   0 _col0 (type: int)
                   1 _col0 (type: int)
 Stage: Stage-3
   Map Reduce
     Map Operator Tree:
         TableScan
           alias: b
           Statistics: Num rows: 6 Data size: 75 Basic stats: COMPLETE Column stats: NONE
           Filter Operator
             predicate: (id > 2) (type: boolean)
             Statistics: Num rows: 2 Data size: 25 Basic stats: COMPLETE Column stats: NONE
             Select Operator
               expressions: id (type: int), user_name (type: string)
               outputColumnNames: _col0, _col1
               Statistics: Num rows: 2 Data size: 25 Basic stats: COMPLETE Column stats: NONE
               Map Join Operator
                 condition map:
                      Inner Join 0 to 1
                 keys:
                   0 _col0 (type: int)
                   1 _col0 (type: int)
                 outputColumnNames: _col0, _col2
                 Statistics: Num rows: 2 Data size: 27 Basic stats: COMPLETE Column stats: NONE
                 Select Operator
                   expressions: _col0 (type: int), _col2 (type: string)
                   outputColumnNames: _col0, _col1
                   Statistics: Num rows: 2 Data size: 27 Basic stats: COMPLETE Column stats: NONE
                   File Output Operator
                     compressed: false
                     Statistics: Num rows: 2 Data size: 27 Basic stats: COMPLETE Column stats: NONE
                     table:
                         input format: org.apache.hadoop.mapred.SequenceFileInputFormat
                         output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat
                         serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
     Local Work:
       Map Reduce Local Work
 Stage: Stage-0
   Fetch Operator
     limit: -1
     Processor Tree:
       ListSink

大家有什么發現,除了表別名不一樣,其他的執行計劃完全一樣,都是先進行 where 條件過濾,在進行 join 條件關聯。說明 hive 底層會自動幫我們進行優化,所以這兩條sql語句執行效率是一樣的

以上僅列舉了3個我們生產中既熟悉又有點迷糊的例子,explain 還有很多其他的用途,如查看stage的依賴情況、排查數據傾斜、hive 調優等,小伙伴們可以自行嘗試。

<上一頁  1  2  3  4  5  6  下一頁>  余下全文
聲明: 本文由入駐維科號的作者撰寫,觀點僅代表作者本人,不代表OFweek立場。如有侵權或其他問題,請聯系舉報。

發表評論

0條評論,0人參與

請輸入評論內容...

請輸入評論/評論長度6~500個字

您提交的評論過于頻繁,請輸入驗證碼繼續

暫無評論

暫無評論

    人工智能 獵頭職位 更多
    掃碼關注公眾號
    OFweek人工智能網
    獲取更多精彩內容
    文章糾錯
    x
    *文字標題:
    *糾錯內容:
    聯系郵箱:
    *驗 證 碼:

    粵公網安備 44030502002758號