前天遇到这样一个场景:
Hive数据仓库里有一个用户访问日志表my_table,这个表对日期字段date (String类型)进行了分区。此外,这个表还有一个字段page_id(Int类型),假定page_id = 9是一个特殊页面。
比如我现在要针对日期范围2014-03-17到2014-03-23内去查询每天的UV,并且需要排除对以上提到的特殊页面的统计。那我这样写:
select
`date`,
count(distinct user_id) as uv
from my_table
where `date` >= 2014-03-17 and `date` <= 2014-03-23 and page_id != 9 or page_id is null
group by `date`
在作不等于限定的时候,我们还需要考虑null值,就是需要把null值也纳入计算中。上面这个查询逻辑会出现内存溢出异常:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
然后回去看脚本,就这么几行,看来看去,以为是查询环境出了问题,然后又试了几次,还是报同样的异常;稍微修改写法,但是自己心里都明白,修改的都应该是无关痛痒的东西。。。果然还是没解决问题,到后来报的异常还稍有变化,出现GC过载神马的了:
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
最后,只好邮件寻求数据平台组同事的帮助,结果人家马上就回复:注意后面的括号,也就是说,where子句应该这样写:
where `date` >= 2014-03-17 and `date` <= 2014-03-23 and (page_id != 9 or page_id is null)
哇嚓,恍然大悟,醍醐灌顶的感觉啊!Hive会把我写的条件解析为
(`date` >= 2014-03-17 and `date` <= 2014-03-23 and page_id != 9) or (page_id is null)
也就是说,OR之后的条件独立于OR之前的条件,并没作分区限定!所以它直接要去取每一个分区的数据,做全表扫描啊!内存当然不够咯。
之前领教过编程语言里运算符结合方向的坑,领教过逻辑运算符的短路计算原则的坑,今天在HQL里领教了WHERE过滤子句的坑。。。不过,都是好坑!
果然,OR运算的存在,不容忽视。