從SQL到HiveQL應改變的幾個習慣

2009-10-30 @ taobaoweb

引言

HiveQL很是像SQL,但兩者並不是等價,若不注意期間的一些差別,容易致使HiveQL的語義錯誤,或下降運行效率等問題。本文將逐步聚集HiveQL應用中發現的差別,整理與此以便查閱。更多HiveQL的語法知識見http://wiki.apache.org/hadoop/Hive/LanguageManualsql

別名

SQL中對列取別名,可以下應用:express

SELECT user_type type FROM s_auction_auctions;

但Hive解析上述語句時,會提示:apache

FAILED: Parse Error: line 1:7 cannot recognize input 'user_type' in select expressionoop

此時,應改成:url

SELECT user_type AS type FROM s_auction_auctions;

虛擬列

Hive中有個"虛擬列"的概念,此列並未在表中真正存在,其用意是爲了將Hive中的表進行分區(partition),這對每日增加的海量數據存儲而言是很是有用的。爲了保證HiveQL的高效運行,強烈推薦在where語句後使用虛擬列做爲限定。拿web日誌舉例,在Hive中爲web日誌建立了一個名爲web_log表,它有一個虛擬列logdate,web_log表經過此列對每日的日誌數據進行分區。所以,在對web_log表執行select時,切記要在where後加上logdate的限定條件,以下:spa

SELECT url FROM web_log WHERE logdate='20090603';

如果沒有logdate做爲限定,Hive默認查詢web_log表的全部分區,有多少天就查多少天,那個場景沒法想象日誌

陷阱
select * from r_winner_details r join t_users s on r.seller_id=s.user_id where r.pt='20091029000000'

由於上句的含義是將r_winner_details表的數據與t_users表數據賣家的數字id進行join, 以後篩選出pt爲1029那天的分區結果.
所以語句一旦運行, 會致使r_winner_details全部分區數據被加載, 致使Map數上萬.
請切記, 正確的寫法是:code

select * from (select * from r_winner_details where pt='20091029000000') r join t_users s on r.seller_id=s.user_id

IN

SQL中可使用IN操做符來規定多個值:hadoop

SELECT * FROM Persons WHERE LastName IN ('Adams','Carter');

HiveQL目前是不支持IN操做符的,須要經過轉換爲多個OR鏈接的條件:

SELECT * FROM Persons WHERE LastName = 'Adams' OR LastName = 'Carter';

INNER JOIN

SQL中對兩表內聯能夠寫成:

SELECT a.col, b.col FROM t1 a, t2 b WHERE a.id=b.id;

但這在HiveQL中是不支持的,需轉爲JOIN關鍵字的寫法,如:

SELECT a.col, b.col FROM t1 a JOIN t2 b ON a.id=b.id;
OutOfMemory
在JOIN的實踐中,時常發現OutOfMemory的異常,或是task"跑不動"的狀況,總結髮現,一旦這類問題出現,他們JOIN的key的值多半是異常的(亂碼或是null),所以在應用的過程尤爲要注意過濾掉異常的key數據。
若不是異常數據,可嘗試調換join兩表先後順序解決。

分號字符

分號是SQL語句結束標記,在HiveQL中也是,可是在HiveQL中,對分號的識別沒有那麼智慧,例如:

SELECT concat(property,concat(';',zoo))  FROM auctions;

這個語句嘗試將商品表中的兩個屬性相關的字段值用分號進行鏈接,但HiveQL在解析語句時提示:

FAILED: Parse Error: line 0:-1 mismatched input '<EOF>' expecting ) in function specification

能夠推斷,Hive解析語句的時候,只要遇到分號就認爲語句結束,而不管是否用引號包含起來。
解決的辦法是,使用分號的八進制的ASCII碼進行轉義,那麼上述語句應寫成:

SELECT concat(property,concat('\073',zoo))  FROM auctions;
八進制ASCII碼
本人嘗試了用十六進制的ASCII碼,但Hive會將其視爲字符串處理並未轉義,貌似僅支持八進制,緣由不詳。這個規則也適用於其餘非SELECT語句,如CREATE TABLE中須要定義分隔符,那麼對不可見字符作分隔符就須要用八進制的ASCII碼來轉義。

Insert

HiveQL中Insert的做用不一樣於SQL中的, 那麼經過HiveQL中實現新增數據該如何作呢?
假設Hive中有表p1,

hive> DESCRIBE p1;
OK
id      int
value   int
hive> SELECT * FROM p1;
OK
3       4
1       2
2       3

現增長一條記錄:

hive> INSERT OVERWRITE TABLE p1 
      SELECT id, value FROM (
          SELECT id, value FROM p1 
          UNION ALL 
          SELECT 4 AS id, 5 AS value FROM p1 limit 1
      ) u;

結果是:

hive>SELECT * FROM p1;                                
OK
3       4
4       5
2       3
1       2

其中的關鍵在於, 關鍵字UNION ALL的應用, 即將原有數據集和新增數據集進行結合, 而後重寫表.

列陷阱
次序

INSERT OVERWRITE TABLE在插入數據時,是按照後面的SELECT語句中的字段順序插入的. 也就說, 當id 和value 的位置互換, 那麼value將被寫入id, 同id被寫入value.

初始值

INSERT OVERWRITE TABLE在插入數據時, 後面的字段的初始值應注意與表定義中的一致性. 例如, 當爲一個STRING類型字段初始爲NULL時:

NULL AS field_name // 這可能會被提示定義類型爲STRING, 但這裏是void

CAST(NULL AS STRING) AS field_name // 這樣是正確的

又如, 爲一個BIGINT類型的字段初始爲0時:

CAST(0 AS BIGINT) AS field_name

Merge

如今經過下列語句模擬須要Merge的數據集:

hive> SELECT id, value FROM (
          SELECT id, (value-1) AS value FROM p1 WHERE (id%2)=0 
          UNION ALL 
          SELECT 5 AS id, 6 AS value FROM p1 limit 1
      ) u;
OK
4       4
2       2
5       6

下面用這個結果集合併到p1中:

hive> INSERT OVERWRITE TABLE p1 
      SELECT 
          coalesce(n.id, o.id), 
          coalesce(n.value, o.value) 
      FROM p1 o 
      FULL OUTER JOIN (
          SELECT id, value FROM (
              SELECT id, (value-1) AS value FROM p1 WHERE (id%2)=0 
              UNION ALL 
              SELECT 5 AS id, 6 AS value FROM p1 limit 1
          ) u 
       ) n ON o.id=n.id;
hive> SELECT * FROM p1;
OK
1       2
2       2
3       4
4       4
5       6
Full or Left
Merge是選擇Full Outer Join仍是Left Outer Join? 這須要開發者對更新的數據比較瞭解.
一般, 要更新的數據若只有對已有數據的部分更新, 則應選用Left Outer Join; 而當要更新的數據有新數據須要增長時, 則應選用Full Outer Join.

IS [NOT] NULL

SQL中null表明空值, 值得警戒的是, 在HiveQL中String類型的字段如果空(empty)字符串, 即長度爲0, 那麼對它進行IS NULL的判斷結果是False.

相關文章
相關標籤/搜索