Hive通過查詢語句向表中插入數據過程中發現的坑


前言

近期在學習使用Hive(版本號0.13.1)的過程中,發現了一些坑,它們也許是Hive提倡的比關系數據庫更加自由的體現(同一時候引來一些問題)。也許是一些bug。總而言之,這些都須要使用Hive的開發者額外注意。本文旨在列舉我發現的3個通過查詢語句向表中插入數據過程中的問題,希望大家注意。

數據准備

為了驗證接下來出現的問題,須要先准備兩張表employees和staged_employees。並准備好測試數據。首先使用下面語句創建表employees:

  create table employees (
    id int comment 'id',
    name string comment 'name')
  partitioned by (country string, state string)
  row format delimited fields terminated by ',';

employees的結構比較簡單,有id、name、country、state四個字段,當中country和state都是分區字段。特別須要提醒的是這里顯示的給行格式指定了字段分隔符為逗號。因為默認的字段分隔符\001不便於筆者准備數據。然后創建表staged_employees:

  create table staged_employees (
    id int comment 'id',
    user_name string comment 'user name')
  partitioned by (cnty string, st string);

staged_employees也有4個字段,除了字段名不同之外,和employees的4個字段的含義是同樣的。

我們首先使用下面語句給employees的country等於US,state等於CA的分區載入一些數據:

  load data local inpath '${env:HOME}/test.txt'
  into table employees
  partition (country = 'US', state = 'CA');

再給employees的country等於CN。state等於BJ的分區載入一些數據:

  load data local inpath '${env:HOME}/test2.txt'
  overwrite into table employees
  partition (country = 'CN', state = 'BJ');
以上語句的運行過程如圖1所看到的。


圖1 employees載入數據

最后我們看看employees中准備好的數據,如圖2所看到的。


圖2 employees中准備好的數據

INSERT OVERWRITE的歧義

因為staged_employees中還沒有數據。所以我們查詢employees的數據,並插入staged_employees中:

insert overwrite table staged_employees
partition (cnty = 'US', st = 'CA')
select * from employees e
where e.country = 'US' and e.state = 'CA';
大家看看這條sql有沒有問題?終於的運行結果如圖3所看到的。

圖3 SemanticException [Error 10044]
因為圖3中的文字太小,這里把這些錯誤提示信息列在下邊:

FAILED: SemanticException [Error 10044]: Line 1:23 Cannot insert into target table because column number/types are different ''CA'': Table insclause-0 has 2 columns, but query has 4 columns.
我們的sql應該沒有問題吧?細致查看提示信息。說是“表僅僅有2列。可是查詢有4列”。剛才說過,我建的兩張表除了字段名稱的差異。其結構全然一樣。兩張表都有4個字段(2個普通字段和2個分區字段),為什么說 staged_employees僅僅有2列呢?這是因為Hive遵循讀時模式且遵循相對寬松的語法,在插入或裝載數據時。不會驗證數據與表的模式是否匹配。

僅僅有在讀取數據時才會驗證。因此在向表staged_employees插入數據時不會驗證。而查詢讀取employees表中的數據時會驗證。我對sql進行了調整,調整后的清單例如以下:

insert overwrite table staged_employees
partition (cnty = 'US', st = 'CA')
select e.id, e.name from employees e
where e.country = 'US' and e.state = 'CA';
運行這條sql的過程如圖4所看到的。



圖4 正確運行insert overwrite

我們看看staged_employees表中,如今有哪些數據(如圖5所看到的):


圖5 staged_employees中的數據

熟悉MySQL等關系型數據庫的同學可能要格外注意此問題了。

FROM ... INSERT ... SELECT的歧義

本節正式開始之前,向employees表中再載入一些數據:

  load data local inpath '${env:HOME}/test3.txt'
  into table employees
  partition (country = 'CA', state = 'ML');
運行上面sql的過程如圖6所看到的。


圖6 載入新的數據

這時表employees的數據如圖7所看到的。


圖7

Hive提供了一種特別的INSERT語法。我們最好還是先看看其使用方式,sql例如以下:

from employees e
insert into table staged_employees
partition (cnty = 'CA', st = 'ML')
select * where e.country = 'CA' and e.state = 'ML';
運行這條sql的過程如圖8所看到的。


圖8 SemanticException [Error 10044]

能夠看到這里再次出現了之前提到的問題,我們依舊依照之前的方式進行改動,sql例如以下:

from employees e
insert into table staged_employees
partition (cnty = 'CA', st = 'ML')
select e.id, e.name where e.country = 'CA' and e.state = 'ML';
如今運行這條sql,發現能夠成功運行,如圖9所看到的。


圖9

如今來看看staged_employees中的數據(如圖10所看到的),看來的確將分區數據插入了。


圖10 staged_employees中的數據

FROM ... INSERT ... SELECT存在bug

我們繼續使用FROM ... INSERT ... SELECT語法向staged_employees中插入數據,sql例如以下:

from employees e
insert into table staged_employees
partition (cnty = 'US', st = 'CA')
select e.id, e.name where e.country = 'US' and e.state = 'CA';
這條sql非常明顯是向staged_employees中再次插入country等於US。state等於CA分區的數據,依據INSERT INTO的通常含義。應當是向表中追加。我們運行這段sql來驗證一下,如圖11所看到的。


圖11

我們看看這時staged_employees中的數據。如圖12所看到的。


圖12

的確印證了,INSERT INTO是用於追加的。

我們將sql進行調整,即將INSERT INTO改為INSERT OVERWRITE:

from employees e
insert overwrite table staged_employees
partition (cnty = 'US', st = 'CA')
select e.id, e.name where e.country = 'US' and e.state = 'CA';
運行這條sql的過程如圖13所看到的。


圖13

我們看看這時staged_employees中的數據,如圖14所看到的。



圖14

這說明INSERT OVERWRITE是用於覆蓋的。

依據官方文檔說明,這樣的FROM ... INSERT ... SELECT語法中的INSERT ... SELECT是能夠有多個的。於是我編寫下面sql,用來向表staged_employees中覆蓋“country等於CA。state等於ML”分區的數據。而且覆蓋“country等於US。state等於CA”分區的數據。

from employees e
insert overwrite table staged_employees
partition (cnty = 'US', st = 'CA')
select e.id, e.name where e.country = 'US' and e.state = 'CA'
insert overwrite table staged_employees
partition (cnty = 'CA', st = 'ML')
select e.id, e.name where e.country = 'CA' and e.state = 'ML';

運行以上sql的過程如圖15所看到的。


圖15

因為都是覆蓋更新,所以staged_employees中的數據並未發生改變。

依據官方文檔。以上sql中還能夠將INSERT OVERWRITE和INSERT INTO進行混用,sql例如以下:

from employees e
insert overwrite table staged_employees
partition (cnty = 'US', st = 'CA')
select e.id, e.name where e.country = 'US' and e.state = 'CA'
insert into table staged_employees
partition (cnty = 'CN', st = 'BJ')
select e.id, e.name where e.country = 'CN' and e.state = 'BJ';

這段sql將覆蓋“country等於US。state等於CA”分區的數據。而且追加“country等於CN,state等於BJ”分區的數據。

運行這段sql的過程如圖16所看到的。


圖16

最后。我們來看看staged_employees中的數據,如圖17所看到的。


圖17

從圖17中看到,“country等於CN。state等於BJ”分區的數據如我們所願追加到表staged_employees中了。“country等於US。state等於CA”分區的數據並沒有被覆蓋。而是追加。

這非常明顯是一個bug,希望大家注意!


后記:個人總結整理的《深入理解Spark:核心思想與源代碼分析》一書如今已經正式出版上市,眼下京東、當當、天貓等站點均有銷售,歡迎感興趣的同學購買。


京東:http://item.jd.com/11846120.html 

當當:http://product.dangdang.com/23838168.html 




免責聲明!

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



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