2. Hive常見操作命令整理


該筆記主要整理了《Hive編程指南》中一些常見的操作命令,大致如下(持續補充中):

1. 查看/設置/修改變量
2. 執行命令
3. 搜索相關內容
4. 查看庫表信息
5. 創建表
6. 分區
7. 修改表(重命名、修改列、刪除列、增加列)
8. 找到表位置並導出至本地
9. 去空格
10. case...when...then句式
11. 操作符
12. group by...having句式
13. JOIN
14. order by和sort by
15. 抽樣查詢
16. 視圖
17. 分桶表
18. 函數
19. with...as
20. rank() over (partition by ... order by ... asc/desc)和row_number()
21. 時間相關的函數(from_unixtime,unix_timestamp,date_add,months_between)
22.substr()
23.regexp_replace()和regexp_extract()
24.動態分區
25.nvl和grouping set
26. concat_ws()、group_concat()和collect_list()
27.lateral view explode()

 

1. 查看/設置/修改變量

$ hive

############
# 查詢環境變量
############
hive>set env:HOME;
# 打印命名空間hivevar, hiveconf, system和env所有變量
hive>set;
# 還打印Hadoop所定義的所有屬性
hive>set -v;

############
# 設置hive變量
############
hive>set hivevar:foo=bar
# 查看剛設置的變量
hive>set foo;
hive>set hivevar:foo;

############
# 修改屬性變量
############
# hiveconf: Hive相關的配置屬性
# 不進入Hive進行配置屬性修改
hive --hiveocnf hive.cli.print.current.db=true
# 進入hive進行配置修改
hive>set hiveconf:hive.cli.print.current_db=true;

 

2. 執行命令

# 執行命令方式1:使用下面的 “一次使用”命令(-e是指一次執行,-S是指靜默模式,在輸出結果中不顯示Ok和Time taken字段)
hive -e -S "select * from mytable limit 3";

# 執行命令方式2:調用Hive執行hql文件
hive -f /path/query.hql

# 執行命令方式3:在Hive shell內執行hql文件
$ hive
hive>source /path/query.hql

 

3. 搜索相關內容

# 模糊搜索set命令的輸出結果中某個於warehouse相關的屬性
$ hive -S -e "set" | grep warehouse
hive.metastore.warehouse.dir=/user/hive/warehouse
hive.warehouse.subdir.inherit.perms=false

 

4. 查看庫表信息

# 查看數據庫,使用like是以ahf開頭,以其他字符結尾(即.*)的數據庫名
show databases;
show databases like 'ahf.*'; # 查看表的詳細表結構信息(formatted比extended輸出內容更詳細且可讀性更高) describe mydb.table; describe formatted mydb.table; describe extended mydb.table;

 

5. 創建表

# 拷貝表結構,而不拷貝數據(用like)
create table if not exists mydb.mytable like mydb.mytable2;

# 拷貝表結構,且拷貝數據(用as)
create table if not exists mydb.mytable as
select * from mydb.mytable2;

# 直接創建表結構
create table if not exists mydb.mytable
(
    id         string comment 'id',
    name    string comment '姓名'           
)
partitioned by (class string)
stored as orc;

 

6. 分區

# 建立分區
create table
(字段1 字段類型, 字段2 字段類型)
paritioned by (字段名3 字段類型, 字段名4 字段類型);

# 查看分區
show partitions table_name;

# 查看某個特定分區鍵的分區
show partitions table_name partition(一個分區字段='該分區字段下的某個值');

# 增加分區
alter table log_message add partition(year=2012, month=1, day=2);

# 刪除分區
alter table log_messages drop if exists partition(year=2012, month=1, day=2);

 

7. 修改表(重命名、修改列、刪除列、增加列)

# 表重命名
alter table log_messages rename to logmsgs;

# 修改列信息, 在下面的例子中,
# 我們將字段hms重命名為hour_minutes_seconds,修改其類型和注釋,再轉移到severity字段之后
# 如果用戶想將這個字段移動到第一個為位置,只需要使用first關鍵字替換after severity
alter table log_messages change column hms hour_minutes_seconds int
comment 'the hours, minutes, and seconds parts of the timestamp'
after severity;

# 刪除或替換列, 下面的例子移除了之前所有字段並重新指定了新的字段
alter table log_messages replace columns
(
   新字段1    int        comment '...',
   新字段2    string    comment '...'
);

# 增加列
alter table log_messages add columns(
app_name string comment 'application name',
session_id long comment 'the current session id'
);

 

8. 找到表位置並導出至本地

# 找到表位置
describe formatted mydb.mytable;
# 例如:Location:
hdfs://alg-hdfs/warehouse/user/alvinai.mydb/mytable

# 導出文件
hadoop fs -cp [表來源] [目標導出路徑]

 

9. 去空格

# 去空格 ltrim去左空格,rtrim去右空格
ltrim(string s)

 

10. case...when...then句式

select name,
         salary,
         case when salary < 5000.0 then 'low'
              when salary >= 5000.0 and salary < 70000.0 then 'middle'
              else 'high'
              end as bracket
from employees;

 

11. 操作符

# 常見的謂語操作符
A<>B跟A!=B是一樣的
A<==>B是指任一為NULL,則結果為NULL
A is null還有A is not null
a not between b and c, between是閉區間

# LiKE和RELIKE謂語操作符
A like B, A relike B前者是SQL,后者是JAVA的正則表達式
A like B, ‘x%’表示A必須以字母x開頭,‘%x’表示A必須以字母x結尾,‘%x%’表示A包含字母x,可以位於開頭結尾或者字符串中間
A relike B, .號表示任意字符,*表示重復左邊的字符串零次到無數次,表達式(x|y)表示x或者y匹配
例子:查找住址以Ave結尾的人
select name, address from where employees where address like 'Ave.';
例子:查找地址以0開頭的人
select name, address from where employees where address like 'O%';

# split操作符
select split(row_key, '_')[0] as account_id

 

12. group by...having句式

# 如果想要對group by語句產生的分組進行條件過濾,可以用having例如:
select year(ymd),
       avg(price_close) from stocks
where exchange = 'NASDAQ' and symbol = 'AAPL'
group by year(ymd)
having avg(price_close) > 0.0;

 

13. JOIN

Hive連接有:

  • inner join: 交集,就是join。擴展:MySQL交集用inersect,Hive用inner join。
  • left outer join: 就是left join。
  • right outer join: 就是right join。
  • full outer join: 並集,就是全外連接。
  • left semi join:左半開連接,Hive沒有右半開連接,它與left join的差異就是對待右表中重復key的處理方式差異:因為 left semi join 是 in(keySet) 的關系,遇到右表重復記錄,左表會跳過,而 left join on 則會一直遍歷,這就是left outer join和left semi join的區別。
  • join:笛卡爾積連接。
  • union all:將2個或多個表進行合並,每一個union子查詢都必須具有相同列,而且對應的每個字段的字段類型必須是一致的。
  • 其它連接:map-side JOIN(Hive在map端執行連接過程,將小表完全放到內存中),sort-merge JOIN(分類-合並連接)。
union all的例子:
select log.ymd, log.level, log.message
from(
    select l1.ymd, l1.level, l1.message, 'Log1' as source from log1 l1
    union all
    select l2.ymd, l2.level, l2.message, 'Log2' as source from log2 l2
) log
sort by log.ymd asc

使用JOIN時要注意:

  • 由於表之間關聯關系是一對多時,出現的數據重復問題。例如如果右表有重復數據(即多條數據對應左邊表一條數據),那么重復數據會全部保留。例如使用left join是連接兩表(左表A,右表B), 它會保留左表A有數據但是右表B為空的數據也就是說只要右表B重復數據(多條數據對應左邊表一條數據),那么會全部保留。
  • inner join也可能導致數據重復,如果是一對以關系,使用后也請注意查重確保正常連接,在使用過程中建議在select后加distinct或者group by去重下。

 

14. order by和sort by

  • order by對查詢結果進行全局排序
  • sort by對查詢結果進行局部排序,因為它只會在每個reducer中對數據進行排序,但並非全局有序。

如果reducer數量只有一個,sort by跟order by得到的結果一樣,而且速度更快。
distribute by規定map端如何分發數據到同一個reducer中進行處理,然后使用sort by對數據進行排序。Hive要求distribute by語句要寫在sort by前面。簡單來說sort by得搭配distribute by一起使用。例子如下:

select s.ymd, s.symbol, s.price_close
from stocks s
distributed by s.symbol
sort by s.symbol asc, s.ymd asc;

 另外,cluster by等價於distribute by和sort by的搭配,但只允許降序排序。使用distribute by... sort by語句或者簡化版的cluster by語句會剝奪sort by的並行性,但可以實現輸出文件的數據是全局排序。

 

15. 抽樣查詢

抽樣有兩種:隨機抽樣分桶抽樣數據塊抽樣

隨機抽樣:使用order by或cluster都行。

# 使用order by,即全局隨機抽樣
hive>create table mytable as
select *
from mytable2
order by rand()
limit 100;

# 使用cluster by,即局部隨機抽樣,這比sort by快,因為不需要distribute by rand() 和sort by rand() 進行了兩次隨機,cluster by rand() 僅一次隨機
hive>create table mytable as
select *
from mytable2
cluster by rand()
limit 10000;

分桶抽樣:舉個例子,假設numbers表只有number字段,其值是1-10。我們可以用下面的分桶語句來抽樣,分桶語句中的分母表示的是數據將會被散列的桶的個數,而分子表示將會選擇的桶編號。簡單來說它把10個數分到2個桶里,1個桶就是5個數據。

hive>select * from numbers tablesample(bucket 1 out of 2 on number) s;
2
4
6
8
10
hive>select * from db.table tablesample(bucket 1 out of 10 on rand());

數據塊抽樣:按照抽樣百分比(至數據大小占比,而不是行數) tablesample(n percent) /大小 tablesample(n M) /行數 tablesample(n rows)進行抽樣。這種抽樣的最小抽樣單元是一個HDFS數據塊,如果表的數據大小小於普通塊大小128MB的話,那么將會返回所有行。

hive>select * from numbersflat tablesample(0.1 percent) s;

  tablesample(n rows) 實際返回的行數可能會有很大差異,具體原因可參考, TABLESAMPLE returns wrong number of rows?,原因在上面也解釋了,涉及數據塊大小的因素。為了解決這個可以考慮使用下面的代碼:

select * from (select * from table tablesample(1000 rows)) a limit 1000

*注意: select TOP 10000 * from tabData TABLESAMPLE(10000 ROWS) REPEATABLE(100); 不能用,因為Hive無法識別TOP和REPEATABLE操作詞,它們是SQL Server的語法。另外,Hive在使用嵌套子查詢是要記得加別名,如上面的a。

 

16. 視圖

 創建視圖來降低查詢復雜度,避免用戶通過重用重復的部分來構建復雜的查詢,能幫助減少多重嵌套子查詢。

# 創建視圖
create view shorter join as
select * from people join cart
on cart,people_id=people_id
where firsetname='john';

# 刪除視圖
drop view if exists view_name;

 

17. 分桶表

# 建立分桶表
# 例如使用user_id作為分桶字段,則字段值會根據用戶指定的值進行哈希分發到同種。同一個user_id下的記錄通常會存儲到同一個桶內。假設用戶數要比桶數多,那么每個桶就會包含多個用戶的記錄
create table weblog (user_id int, url string, source_ip string)
partitioned by (dt string)
clustered by (user_id) into 96 buckets;

# 給分桶表插入分區數據 - 為了強制Hive為目標表的分桶初始化設置一個正確的reducer個數
hive> set hive.enfore.bucketing = true;
hive> from raw_logs
insert overwrite table weblog
partition (dt='2009-02-25')
select user_id, url, source_ip where dt='2009-02-25';

給分桶表插入數據 - 如果沒有使用hive.enfore.bucketing,我們就需要自己設置和分桶個數相匹配的reducer個數,例如
 set mapred.reduce.tasks=96; 。然后再insert語句中,需要在select語句后增加cluster by語句。

之所以要給分桶表做以上的操作,是因為對於所有表的元數據,指定分桶並不能保證表可以正確地填充,用戶可以根據前面的示例來確保是否正確地填充了表。分桶地好處在於:有利於執行高效地map-side join

 

18. 函數

UDF是指用戶自定義函數,顯示當前Hive會話所加載的所有函數名稱。

show functions;

# 展示concat函數的介紹
describe function concat;
describe function extended concat;

 

19. with...as

with t1 as (...),
t2 as (...)
select ...
from ...
where

 

20. rank() over (partition by ... order by ... asc/desc)和row_number()

OVER()窗口函數常搭配聚合函數(例:sum(), rank()等)。OVER():指定分析函數工作的數據窗口大小,決定了聚合函數的范圍。舉個簡單的例子:我們想知道各班級下,各小組里,同學的成績排名。

rank() over(parition by class_no, group_no order by student_grade desc) as student_group_rank

rank()允許並列相同排名出現,而若要順序排名而不並列排名的話,可以采用row_number(),使用方法跟rank()一致。 

 

21. 時間相關的函數(from_unixtime,unix_timestamp,date_add)

如果我想將時間‘20200320‘加一天並轉為’20200321‘該怎么辦?轉換前先了解下面三個函數:

 UNIX_TIMESTAMP(date)  :一個DATE轉為Unix timestamp時間格式。

 DATE_ADD()  :函數向日期添加指定的時間間隔,只支持’yyyy-mm-dd‘格式的操作

 FROM_UNIXTIME(unix_timestamp,format) :返回表示Unix時間標記的一個字符串,根據format字符串格式化。

完整轉換代碼(先將20200320轉為unix timestamp->轉為2020-03-20格式->加一天為2020-03-21->轉為20200321):

from_unixtime(unix_timestamp(date_add(from_unixtime(unix_timestamp(’20200320‘,'yyyymmdd'),'yyyy-mm-dd'),1),'yyyy-mm-dd'),'yyyymmdd')

 那如果想找到兩個時間之間的月份間隔長度,可以使用 MONTHS_BETWEEN 函數:

floor(months_between(from_unixtime(unix_timestamp(start_dayno,'yyyymmdd'),'yyyy-mm-dd'),from_unixtime(unix_timestamp(end_dayno,'yyyymmdd'),'yyyy-mm-dd'))) as time_period

 

22. substr()

 substr(待截取字符串,截取起始點,截取長度) ,注意Hive的字符串起點是1,而不是0。另外, substr() 結果與另外一個數做比較時,會忽略掉首位空格。例如:  substr(2020/7/29 9:13', 1, 10) ='2020/7/29 '與'2020/7/29'相等。

 

23. regexp_replace()和regexp_extract()

regexp_replace(rtrim(substr(log_map['StartTime'],1,10)),'/','') as dayno

select處將結尾空格去掉,替換/分隔符為空。即2020/07/29 22:56:44 -> 20200729。

regexp_extract('@temperature@23''', '(@temperature@)(.*?)(\'])', 2) as temperature

 

24. 動態分區

如果你需要將從一個分區大表里抽取數據放置另外一張新表且新表要為分區表(源表和新表),那怎么辦?

  • 首先,建新表表結構(若源表和新表表結構沒區別,可以用 create table mydb.mytable like source_db.table );
  • 然后插入數據有兩種方式,第一種是構建調度,一天天寫入分區。第二種是使用動態分區,設置動態分區字段,讓Hive自動根據分區字段插入對應分區的數據進去。第二種方法比較省事,因為不需要建調度。
set hive.exec.dynamic.partition=true;
set hive.exec.max.dynamic.partitions=10000;
set hive.exec.dynamic.partition.mode=nonstrict;
insert overwrite table mydb.mytable (partition_col)
select * from source_db.table

需要注意的一件事:當你使用靜態分區時,partition_col不需要寫在字段構建區內,而使用動態分區時,建表要將partition_colx寫在字段構建區最后的位置上,不然動態分區差數會報錯說建表字段數和插入字段數不匹配。

上述代碼有幾個設置需要注意:

 set hive.exec.dynamic.partition=true; :默認為false即靜態分區,若要使用動態分區,請設置true。

 set hive.exec.max.dynamic.partitions=10000; :默認1000。表示一個動態分區語句可以創建的最大動態分區個數,超出報錯。

 set hive.exec.dynamic.partition.mode=nonstrict; :這個屬性默認值是strict, 就是要求分區字段必須有一個是靜態的分區值。

補充:實際使用中,上述方法不是最優法,因為一段源表抽取數據太大,會報錯OOM。所以為了優化,改為靜態方法,用while去依次抽取小部分數據,然后插入一個分區,然后再抽再寫入下個分區。實現偽代碼如下:

# 用shell腳本跑
v_start_dayno='20201010'
v_end_dayno='20201110'

while [ ${v_start_dayno} -le ${v_end_dayno} ]
do

hql="insert overwrite table mydb.mytable (dayno=${v_start_dayno})
select * from source_db.table
where dayno = '${v_start_dayno}';"
執行hql函數

v_start_dayno=`date -d "${v_start_dayno} +1 day" +%Y%m%d`
done

 

25.nvl和grouping set

如果一個表,你想既區分版本字段version,又不區分版本字段統計。可搭配nvl和grouping set進行使用。

 NVL(E1,E2) : 如果E1為NULL,則函數返回E2,否則返回E1本身。

 grouping set() : 簡單說是group by + union all,既group by版本字段version,又不group by它,然后將他們的聚合結果進行並集join。

select day, nvl(version, '不區分'), count(1) as pv
from dr.table
group by day, version
grouping sets((day, version), (day))

注意:grouping sets使用前得加group by且涵蓋所有grouping sets中使用的字段。另外grouping sets后的version,如果區分版本,union all后的version是有具體版本值的,但如果不區分版本,union all后version是null,因此需要使用NVL命令位‘不區分’。

 

26. concat_ws()、group_concat()和collect_list()

如果每個用戶成功/失敗發布一條內容會上傳一條數據,那么表會如下所示:

user_name blog_id blog_status
Alvin 1 success
Alvin 2 fail
Fiona 3 fail
Fiona 4 success

 現在我們需要把它轉為如下格式:

user_name blog_id2status
Alvin {1:success,2:fail}
Fiona {3:fail,4:success}

 實現以上轉換,需要按下列步驟走:

  •  concat_ws(':', blog_id, blog_status) : 用‘:’連接blog_id和blog_status
  •  group_concat(concat_ws(':', blog_id, blog_status) order by blog_id asc separator ',') : 對group by內的數據進行組內合並,合並用‘,’分隔開。注意:只有Mysql可用,Hive和Spark不支持。
  •  concat_ws(',', collect_list(concat_ws(':', blog_id, blog_status))) :用collect_list(不去重)/collect_set(去重)也可實現。
  •  str_to_map() : 將字符串轉為map結構。

完整語句:

select user_name, str_to_map(concat_ws(',', collect_list(concat_ws(':', blog_id, blog_status)))) as blog_id2status
from t
group by user_name

 

27. lateral view explode()

如果現在有個表記錄每個用戶(user_id)下采集的一系列環境光值(env_luxs):

user_id env_luxs
1 12 20 30
2 0 4

我現在希望將其轉化成下表格式:

user_id env_lux
1 12
1 20
1 30
2 0
2 4

要實現如上轉換,需要采用三個函數:

 split(env_luxs. ' ') : 將環境光序列按空格分開,並轉為數組array格式。

 explode() : 將array或者map結構拆分成多行。

 lateral view udtf(expression) : 將UDTF(用戶定義表生成函數, User-Defined Table-Generating Functions)生成的結果放在虛擬表中,然后與輸入行進行join進行連接。 

select imei, env_lux
from t
lateral view explode(split(env_luxs, ' ')) t1 as env_lux

 


免責聲明!

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



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