Hive筆記之導出查詢結果
導出到本地
導出查詢結果到本地:
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筆記之導出查詢結果