1. 程式人生 > >Hive中LIKE查詢使用萬用字元'%'的一個BUG--當轉義符'\'遇到萬用字元'%'或'_'

Hive中LIKE查詢使用萬用字元'%'的一個BUG--當轉義符'\'遇到萬用字元'%'或'_'

在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\\’;