工作中經常遇到使用Hive導出數據到文本文件供數據分析時使用。Hive導出復雜數據到csv等文本文件時,有時會遇到以下幾個問題:
- 導出的數據只有數據沒有列名。
- 導出的數據比較復雜時,如字符串內包含一些制表符、換行符等。直接導出后,其它程序無法對數據進行正常的分割。若直接使用管道符號和sed指令的話,會導致分列出錯。
- 數據分析師使用數據時使用R語言,加載數據時如果一個字段只有單引號或雙引號時,會導致后續數據讀為一行。
- 導出數據時空值在文本顯示為\N,不是NULL。
- hive導出的數據生成若干個000000_0,000001_0這樣的文件,可讀性比較差,需要手工去合並
針對上述幾個問題采取以下解決方案:
- 導出的數據不包含列名,使用hive -e 'use mydb;SET hive.cli.print.header=true; SELECT * FROM t_test LIMIT 0'方式獲取列名。若抓取的為多張表的字段,需要對查詢語句進行相應的調整。
- 根據具體的情況設置合適的分隔符,我這里使用一般極少用到的符號 '|' 作為分隔符。
- 對於出現的單引號和雙引號,在經過分析師溝通后,對這些無實際意義的一個字段只有單引號或雙引號的字段置空。若有其它需求也可以根據具體情況進行替換。
- 使用sed指令對\N字段進行替換。根據具體情況,注意確保不修改到字符串中的\N。可以把'|\N|'替換為'|NULL|',行首行尾的數據也要考慮到。我這里由於其它字段不包含'\N',就簡化處理了。
- 000000_0文件聚合使用腳本進行解決。
下面為一個具體的示例的shell腳本:
1 #!/bin/bash 2 mkdir -p /tmp/project_1010/project #初始化結果存放的目錄 3 hive -e "use mydb; 4 insert overwrite local directory '/tmp/project_1010/t_test/' #指定該表初步查詢結果存放的目錄 5 ROW FORMAT DELIMITED FIELDS TERMINATED BY '|' #指定字段分隔符 6 select t.* from t_test t JOIN #查詢語句 7 t_test1 t1 8 on t1.test_id = t.test_id; 9 " 10 # 生成表頭,替換表頭前的't_test.'字段,並寫入最終的csv文件中 11 hive -e 'use mydb;SET hive.cli.print.header=true; SELECT * FROM t_test LIMIT 0' | sed -e 's/\t/|/g;s/t_test\.//g' > /tmp/project_1010/project/t_test.csv 12 cat /tmp/project_1010/t_test/* >> /tmp/project_1010/project/t_test.csv #把000000_0,000001_0這樣的文件通過追加的方式,寫入最終的csv文件中 13 sed -i 's/\\N/NULL/g' /tmp/project_1010/project/t_test.csv #使用sed處理最終的csv文件,根據需求進行替換 14 sed -i "s/|'|/|NULL|/g" /tmp/project_1010/project/t_test.csv 15 sed -i 's/|"|/|NULL|/g' /tmp/project_1010/project/t_test.csv
本腳本假設抓取項目project中t_test表的數據。考慮到需求的變動,每次生產的項目目錄加一個日期后綴,如“project_1010”。一般一個項目會抓取對個表,針對每個表都會創建一個子目錄存儲生產的000000_0,000001_0等數據。示意圖如下:
1 project_1010/ 2 ├── project #項目名 3 │ ├── t_test.csv 4 │ ├── t_test1.csv 5 ├── t_test #抓取的t_test表數據 6 │ ├── 000000_0 7 │ └── 000001_0 8 ├── t_test1 #抓取的t_test1數據 9 │ ├── 000000_0 10 │ └── 000001_0
先抓取每張需要的表的數據,最后統一匯總在project的目錄下,最終的數據就存儲在project目錄下。
在實際的使用環境中,可以再寫shell腳本和一個配置文件(包括項目名、抓取的表的列表、需要執行的查詢語句)直接生成上述的腳本,不需要一個個的手動生成。