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.