在Hive开发过程中遇到这样一个问题:
例如表T001的字段col1里面存有’ABC\DEF’这样的数据,在Oracle中,我可以通过下面这样的SQL将其查出:
SELECT * FROM T001 WHERE COL1 LIKE ‘ABC\%’;
‘\’不会将’%’进行转义,因为没有使用ESCAPE ‘\’ 语法。
但是到了Hive里就不行了,
SELECT * FROM T001 WHERE COL1 LIKE ‘ABC\%’;
不会得到想要的结果。不过Hive有些特别,因为’\’默认就是转义字符,因此,其实和上面Oracle中等价的写法应该是对’\’转义一次,即:
SELECT * FROM T001 WHERE COL1 LIKE ‘ABC\\%’;
但是很遗憾,还是得不到想要的结果。
为了一探究竟,我做了些简单的实验:
在Hive中插入一条记录(省略部分打印信息):
hive> INSERT OVERWRITE TABLE T001 SELECT '\\' FROM DUAL;
1 Rows loaded to T001
OK
hive> SELECT * FROM T001;
OK
\
然后进行LIKE查询测试:
hive> SELECT * FROM T001 WHERE COL1 LIKE '\\';
OK
\
这一步查询在意料当中,再加入'%'试一下:
hive> SELECT * FROM T001 WHERE COL1 LIKE '%\\';
OK
\
可以看到,将’%’放到前面没有问题,再把’%’放到后面看看行不行:
hive> SELECT * FROM T001 WHERE COL1 LIKE '\\%';
OK
这回问题暴露了,没有查出结果。
我猜想,Hive解析时是用’\’把’%’给转义了,于是测试了一下:
hive> INSERT OVERWRITE TABLE T001 SELECT '%' FROM DUAL;
1 Rows loaded to T001
OK
hive> SELECT * FROM T001;
OK
%
hive> SELECT * FROM T001 WHERE COL1 LIKE '\\%';
OK
%
结果果然不出所料!
但问题是,我现在想让’%’作为通配符,而不想让’\’将其转义,难道就做不到吗?Hive里没有ESCAPE语法,不能通过这种方法解决了,在尝试一下其他方法:既然’\’是转义字符,能把’%’转义掉,那可不可以多加一个’\’把’\’给转义了呢?
SELECT * FROM T001 WHERE COL1 LIKE ‘ABC\\\\%’;
结果还是不行!
于是我又测试了一个小例子:
hive> INSERT OVERWRITE TABLE T001 SELECT '\\\\' FROM DUAL;
1 Rows loaded to T001
OK
hive> SELECT * FROM T001;
OK
\\
hive> SELECT * FROM T001 WHERE COL1 LIKE '\\\\';
OK
\\
恩,不出所料。
hive> SELECT * FROM T001 WHERE COL1 LIKE '%\\';
OK
\\
还是不出所料。
hive> SELECT * FROM T001 WHERE COL1 LIKE '\\%';
OK
这个已经知道查不出来了,因为上面的实验已经证明这个查询匹配到的是’%’
hive> SELECT * FROM T001 WHERE COL1 LIKE '\\\\%';
OK
还是查不出来,进一步实验可以得知这个查询匹配到的是’\%’
于是我得出结论:
‘\’与’%’连在一起用在LIKE中时,’\’总是将’%’转义成普通字符,即’%’失去了通配符的作用。本来’\’转义应该是从左向右解析的,但是当’\’遇到’%’时似乎优先级更高,总是优先和’%’结合。
可想而知,通配符’_’也有同样的问题。
我认为这应该算是Hive的一个BUG。
如果确实想写和Oracle中等价的查询:
SELECT * FROM T001 WHERE COL1 LIKE ‘ABC\\%’;
建议在Hive中用正则实现:
SELECT * FROM T001 WHERE COL1 REGEXP ‘^ABC\\’;