1. 程式人生 > >Hive筆記之導出查詢結果

Hive筆記之導出查詢結果

date htm borde hdf 分區表 bottom -s creat 結果集

導出到本地

導出查詢結果到本地:

INSERT OVERWRITE LOCAL DIRECTORY "/tmp/hive-result/t_visit_video"
SELECT * FROM t_visit_video ;

導出到的本地路徑不必已經存在,會自動創建父目錄,導出的查詢結果會是一個文件夾,文件夾下存放著本次查詢的結果,如果結果集比較大的話會分塊存放。

技術分享圖片

每個數據塊還會有一個CRC校驗文件,此文件為隱藏文件,用於校驗此塊的數據有效性。

但是當查看導出的數據文件時發現悲劇了,列與列之間好像是沒有東西分隔啊:

技術分享圖片

其實是有東西分隔的,這個字符就是^A,只不過這是一個不可見字符,使用cat打印不出,這個字符在vim中可見,使用vim編輯一下它:

技術分享圖片

看到了,列與列之間確實是有一個^A分隔符的。

如果不希望使用^A作為分隔符,可以在導出數據的時候使用ROW FORMAT DELIMITED FIELDS TERMINITED BY ","來指定列與列之間的分隔符,來重新導出一下:

INSERT OVERWRITE LOCAL DIRECTORY "/tmp/hive-result/t_visit_video_002"
ROW FORMAT DELIMITED FIELDS TERMINATED BY ","
SELECT * FROM t_visit_video ;

再查看一下導出的本地文件,發現列與列之間的分隔符是逗號了:

技術分享圖片

也許你會認為自己已經得到了CSV格式,如果這樣的話就踩坑了,因為它並不符合CSV的RFC4180。簡單地來證明一下,在上面的表插入一行巨多逗號然後重新導出:

INSERT INTO t_visit_video PARTITION (day="20180604") SELECT "foo,foo,foo", "bar,bar,bar";
INSERT OVERWRITE LOCAL DIRECTORY "/tmp/hive-result/t_visit_video_003"
ROW FORMAT DELIMITED FIELDS TERMINATED BY ","
SELECT * FROM t_visit_video ;

查看導出結果:

技術分享圖片

註意看最後一行,如果按照CSV的格式去解析的話最後得到的結果一定是錯的,一定要確保所指定的列分隔符不包含在列數據中出現,這是在導出數據指定分隔時需要註意的一個坑。

Hive導出數據時指定分隔符的語法長的令人發指,說實話我是記不住的,這裏可以耍個小聰明,可以先將數據按照默認的分隔符^A導出,然後使用tr將^A替換為想要的分隔符:

技術分享圖片

註意這個^V是先按Ctrl+V,告訴終端我下一個字符要輸入一個特殊字符,然後按CTRL+A打出。

註:使用默認分隔符導出的Hive查詢結果在程序中處理的時候使用split("\x01")或者split("\001")來切分列。

導出到HDFS

導出到HDFS跟導出到本地差不多,只是沒有LOCAL,加LOCAL後面的是本地路徑,否則的話就是HDFS路徑:

INSERT OVERWRITE DIRECTORY "/test/hive-export/t_visit_video" 
SELECT * FROM t_visit_video; 

查看HDFS上導出的目錄:

[root@foobar ~]# hadoop fs -ls /test/hive-export/t_visit_video
Found 1 items
-rwxr-xr-x   1 root supergroup        283 2018-06-08 00:04 /test/hive-export/t_visit_video/000000_0

和導出到本地一樣,只不過是位置換到了HDFS而已。

同樣的,導出到HDFS也可以指定列分隔符:

INSERT OVERWRITE DIRECTORY "/test/hive-export/t_visit_video_002" 
ROW FORMAT DELIMITED FIELDS TERMINATED BY ","
SELECT * FROM t_visit_video;

查看列分隔符是否設置正確:

[root@foobar ~]# hadoop fs -ls /test/hive-export/t_visit_video_002
Found 1 items
-rwxr-xr-x   1 root supergroup        283 2018-06-08 00:12 /test/hive-export/t_visit_video_002/000000_0
[root@foobar ~]# hadoop fs -cat /test/hive-export/t_visit_video_002/000000_0
張三,大唐雙龍傳,20180516
李四,天下無賊,20180516
張三,神探狄仁傑,20180516
李四,霸王別姬,20180516
李四,霸王別姬,20180516
王五,機器人總動員,20180516
王五,放牛班的春天,20180516
王五,盜夢空間,20180516
foo,foo,foo,bar,bar,bar,20180604

導出到其它Hive表

導出到其它表的語法和導出到目錄類似,只是目的地變成了表名,如果目標表是個分區表的話還要指定所要插入的分區。

下面是一個簡單的例子,先復制一份表結構,然後將其中一個分區的數據拷貝一份:

CREATE TABLE t_visit_video_20180516 LIKE t_visit_video;
INSERT OVERWRITE TABLE t_visit_video_copy PARTITION (day="20180516") 
SELECT * FROM t_visit_video WHERE day="20180516";

同樣的,這裏可以使用INTO表示追加到指定的分區,也可以使用OVERWRITE表示覆蓋指定分區。

上面的方式適用於表已經存在的情況,如果想使用一個新表來保存查詢結果但是又不想手動創建的話,可以讓其自動創建表結構:

CREATE TABLE t_visit_video_20180516 AS SELECT * FROM t_visit_video WHERE day=‘20180516‘;

這種方法常用於將查詢結果集導出為臨時表時使用。

定時查詢並備份結果集

hive -e可以用來指定一些命令,hive -f可以用來指定一個腳本文件,可以將導出腳本的邏輯寫個小腳本,借助於crontab定時執行此腳本,即可實現對Hive表查詢並備份。

下面是一個小小的例子,定時將hive表前一天的張三看過的電影導出到某個目錄下,同時記錄操作日誌:

#! /bin/bash

day=`date ‘+%Y%m%d‘ -d ‘day ago‘`
if [ $1 ]; then 
        day=$1
fi
hive="/opt/hive/apache-hive-2.3.3-bin/bin/hive"
dest_dir="/tmp/hive-result/t_visit_video_$day"
log_file="`dirname $0`/backup.log"

echo "[`date ‘+%F %T‘`] $day begin bakcup" >> $log_file
hive -e "INSERT OVERWRITE LOCAL DIRECTORY ‘$dest_dir‘ SELECT * FROM test_003.t_visit_video WHERE day=‘$day‘ AND username=‘張三‘"
echo "[`date ‘+%F %T‘`] $day begin end" >> $log_file

將上面的腳本加入到crontab即可實現定時備份:

0 1 * * * /root/hive/backup/backup.sh

.

Hive筆記之導出查詢結果