最近在建設數據倉庫,處理數據的過程中,經常反覆使用hive的HQL語句,儘管HQL和SQL語言有很多相同之處,但也並不是說HQL就能通用SQL的語法。在使用過程中要尤爲注意。事情經過是這樣的,我在把業務系統數據同步到數倉(數據存儲在Hive)中時,在數據彙總層(DWS),對數據進行彙總處理時,發現有數據丟失的問題,經過排查,發現是在使用 <> 引發的坑。
Hive 中 != 或 <> 致命陷阱
業務場景:把業務數據抽到ODS層(原始數據層)、在DWS層(數據彙總層),對多張多表中的數據進行彙總操作,目的是爲了補全各表的多種維度指標(維表)。
實際操作:因爲是在Hive直接使用HQL語句對多表進行Join的關聯查詢操作,把處理完成的數據寫入到提前建好的表中。跑完SQL以後,對結果數據進行驗證,發現少了數百萬數據,問題極其嚴重(在實際開發過程中,一定要對結果進行多方面的校驗),開始排查問題。
排查問題:首先是對邏輯進行排查。發現邏輯並無錯誤,之後分解HQL,把每個SQL過濾條件單獨拿出來進行驗證,發現問題。 在使用 <> 產生了坑。
問題思考:在數倉建設過程中,因爲工作疏忽,忘記了對ODS原始數據層的數據進行處理。因爲在把ODS原始數據層的數據同步到到DWS數據彙總層時,並沒有經過DWD數據明細層的處理,導致問題出現。
注意:在數倉建設過程,因爲業務數據、或日誌數據、或其他來源的數據。因爲數據往往是很髒亂差的,我們需要對數據進行清洗操作,也就是ETL過程。但是數據倉庫有個指標很重要,就是要把原始數據原封不動的同步到ODS層,在DWD層對數據進行簡單處理。比如補全數據的操作,對NULL或空值進行補值操作。
對!= 或 <>實操驗證
首先,先建一張表,插入數據:
create table if not exists not_eq_temp values(1,22,'小李','男','銷售')(
id int comment 'id',
age int comment '年齡',
name string comment '姓名',
sex string comment '性別',
job string comment '工作'
);
insert into table not_eq_temp values(1,22,'小李','男','銷售');
insert into table not_eq_temp values(2,,'小張','男','');
insert into table not_eq_temp values(3,26,'小麗','女','文員');
insert into table not_eq_temp values(4,22,'小花','女','行政');
insert into table not_eq_temp values(5,25,'小王','男','');
insert into table not_eq_temp values(6,24,'小明','男','銷售');
然後,查詢語句:
select id,age,name,sex,job from not_eq_temp where age <> 22
查詢結果:
| 3| 26|'小麗'|'女'|'文員'|
| 5| 25|'小王'|'男'| ''|
| 6| 24|'小明'|'男'|'銷售'|
可以看出來,id爲4的這行數據,在查詢過程中丟失了。因爲這行數據,年齡沒有采集到,爲空,在使用<>時,會把爲null值的也過濾掉,這顯然不是我們想要的結果。
如何解決使用<>過濾 空值的問題?
方案一
這就需要用到我們前面說的補值操作。在DWD層對缺少或空值的記錄進行補值處理。
具體方式:
select
id,
if(age is null,floor(rand()*100+200),age) AS age,
name,
sex,
job
from
not_eq_temp
注意:因爲這裏age是整數,我們使用floor(rand()*100+200) 來對age進行補值操作。這樣做的好處是,使用rand()隨機函數,有效避免數據傾斜情況的出現。
加200的目的,是爲了跟正常年齡進行區別。在後續數據使用中,當我們看到200歲(目前來說沒人能活200歲)以上的目標時,就能第一時間知道,這是我們補的值,原始業務數據並沒有採集到年齡。
這只是一種情況,大家可以靈活使用。字段類型是字符串或其他類型時,補充對應類型的值就行。千萬注意不要補同樣的值,最好是隨機數。
方案二
如果我們沒有進行DWD層的操作,也就是沒有補值操作。我們在查詢數據的時候,可以使用條件判斷避免出現null值被過濾的情況。
具體方式:
select id ,age ,name ,sex ,job from not_eq_temp where coalesce(age,1) <> 22
coalesce的用法,相當於if(expr is null,expr1,expr2)。
當然還有其他很多方式,我們可以在工作中,自己嘗試。但是還是建議使用第一種方式,在DWD層對髒數據進行處理,因爲這是建設數據倉庫過程中很嚴格的規範要求。數據倉庫中,一般dwd層就是用來對ods層數據進行簡單處理的,如果不發揮這層的作用,那就有點不合時宜了。
使用不等值!= 或<>需要注意
在使用不等值:<>比較或過濾數據時,需要注意以下多種情況。
先來看看<>語法格式:
語法: A <> B
針對所有基本類型,如果表達式A爲NULL,或者表達式B爲NULL,返回NULL;如果表達式A與表達式B不相等,則爲TRUE;否則爲FALSE。
注意:在關係型數據庫中,通常SQL的寫法中不等於也可以這樣寫 != 。但在hive中,當一個string類型和int類型在進行比較的時候會查不出來結果。
數字和數字類型:可以用 != 比較;
帶引號的數字和數字類型:也可以用!= 比較;
帶引號的數字和帶引號數字類型:還可以用 != 比較;
字符串和數字類型:不可以用 != 比較;
字符串和數字類型:不可以用 <> 比較;
總而言之,在使用!= 或 <>比較的時候兩者的字段類型儘量保持一致。