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那天的分區結果. select * from (select * from r_winner_details where pt='20091029000000') r join t_users s on r.seller_id=s.user_id |
SQL中可使用IN操做符來規定多個值:hadoop
SELECT * FROM Persons WHERE LastName IN ('Adams','Carter');
HiveQL目前是不支持IN操做符的,須要經過轉換爲多個OR鏈接的條件:
SELECT * FROM Persons WHERE LastName = 'Adams' OR LastName = 'Carter';
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碼來轉義。 |
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的應用, 即將原有數據集和新增數據集進行結合, 而後重寫表.
如今經過下列語句模擬須要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. |
SQL中null表明空值, 值得警戒的是, 在HiveQL中String類型的字段如果空(empty)字符串, 即長度爲0, 那麼對它進行IS NULL的判斷結果是False.