處理數(shù)據(jù)的過(guò)程中派阱,經(jīng)常反復(fù)使用hive的HQL語(yǔ)句跋涣,盡管HQL和SQL語(yǔ)言有很多相同之處阅懦,但也并不是說(shuō)HQL就能通用SQL的語(yǔ)法和二。在使用過(guò)程中要尤為注意。事情經(jīng)過(guò)是這樣的耳胎,我在把業(yè)務(wù)系統(tǒng)數(shù)據(jù)同步到數(shù)倉(cāng)(數(shù)據(jù)存儲(chǔ)在Hive)中時(shí)惯吕,在數(shù)據(jù)匯總層(DWS)惕它,對(duì)數(shù)據(jù)進(jìn)行匯總處理時(shí),發(fā)現(xiàn)有數(shù)據(jù)丟失的問(wèn)題废登,經(jīng)過(guò)排查怠缸,發(fā)現(xiàn)是在使用 <> 引發(fā)的坑。
Hive 中 != 或 <> 致命陷阱
業(yè)務(wù)場(chǎng)景:把業(yè)務(wù)數(shù)據(jù)抽到ODS層(原始數(shù)據(jù)層)钳宪、在DWS層(數(shù)據(jù)匯總層),對(duì)多張多表中的數(shù)據(jù)進(jìn)行匯總操作扳炬,目的是為了補(bǔ)全各表的多種維度指標(biāo)(維表)吏颖。
實(shí)際操作:因?yàn)槭窃贖ive直接使用HQL語(yǔ)句對(duì)多表進(jìn)行Join的關(guān)聯(lián)查詢操作,把處理完成的數(shù)據(jù)寫入到提前建好的表中恨樟。跑完SQL以后半醉,對(duì)結(jié)果數(shù)據(jù)進(jìn)行驗(yàn)證,發(fā)現(xiàn)少了數(shù)百萬(wàn)數(shù)據(jù)劝术,問(wèn)題極其嚴(yán)重(在實(shí)際開(kāi)發(fā)過(guò)程中缩多,一定要對(duì)結(jié)果進(jìn)行多方面的校驗(yàn)),開(kāi)始排查問(wèn)題养晋。
排查問(wèn)題:首先是對(duì)邏輯進(jìn)行排查衬吆。發(fā)現(xiàn)邏輯并無(wú)錯(cuò)誤,之后分解HQL绳泉,把每個(gè)SQL過(guò)濾條件單獨(dú)拿出來(lái)進(jìn)行驗(yàn)證逊抡,發(fā)現(xiàn)問(wèn)題。 在使用 <> 產(chǎn)生了坑零酪。
問(wèn)題思考:在數(shù)倉(cāng)建設(shè)過(guò)程中冒嫡,因?yàn)楣ぷ魇韬觯浟藢?duì)ODS原始數(shù)據(jù)層的數(shù)據(jù)進(jìn)行處理四苇。因?yàn)樵诎袿DS原始數(shù)據(jù)層的數(shù)據(jù)同步到到DWS數(shù)據(jù)匯總層時(shí)孝凌,并沒(méi)有經(jīng)過(guò)DWD數(shù)據(jù)明細(xì)層的處理,導(dǎo)致問(wèn)題出現(xiàn)月腋。
注意:在數(shù)倉(cāng)建設(shè)過(guò)程蟀架,因?yàn)闃I(yè)務(wù)數(shù)據(jù)、或日志數(shù)據(jù)罗售、或其他來(lái)源的數(shù)據(jù)辜窑。因?yàn)閿?shù)據(jù)往往是很臟亂差的,我們需要對(duì)數(shù)據(jù)進(jìn)行清洗操作寨躁,也就是ETL過(guò)程穆碎。但是數(shù)據(jù)倉(cāng)庫(kù)有個(gè)指標(biāo)很重要,就是要把原始數(shù)據(jù)原封不動(dòng)的同步到ODS層职恳,在DWD層對(duì)數(shù)據(jù)進(jìn)行簡(jiǎn)單處理所禀。比如補(bǔ)全數(shù)據(jù)的操作方面,對(duì)NULL或空值進(jìn)行補(bǔ)值操作。
對(duì)!= 或 <>實(shí)操驗(yàn)證
首先色徘,先建一張表恭金,插入數(shù)據(jù):
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,'小明','男','銷售')遍搞;
SQL語(yǔ)句:
select id,age,name,sex,job from not_eq_temp where age <> 22
查詢結(jié)果:
| 3| 26|'小麗'|'女'|'文員'|
| 5| 25|'小王'|'男'| ''|
| 6| 24|'小明'|'男'|'銷售'|
如何解決使用<>過(guò)濾 空值的問(wèn)題?
方案一:補(bǔ)值
(1)通過(guò)使用 nvl (age, 0)
(2) if(age is null, floor(rand()*100+200), age) AS age
注意:因?yàn)檫@里age是整數(shù)罗侯,我們使用floor(rand()*100+200) 來(lái)對(duì)age進(jìn)行補(bǔ)值操作。這樣做的好處是溪猿,使用rand()隨機(jī)函數(shù)钩杰,有效避免數(shù)據(jù)傾斜情況的出現(xiàn)。
加200的目的诊县,是為了跟正常年齡進(jìn)行區(qū)別讲弄。在后續(xù)數(shù)據(jù)使用中,當(dāng)我們看到200歲(目前來(lái)說(shuō)沒(méi)人能活200歲)以上的目標(biāo)時(shí)依痊,就能第一時(shí)間知道垂睬,這是我們補(bǔ)的值,原始業(yè)務(wù)數(shù)據(jù)并沒(méi)有采集到年齡抗悍。
這只是一種情況驹饺,大家可以靈活使用。字段類型是字符串或其他類型時(shí)缴渊,補(bǔ)充對(duì)應(yīng)類型的值就行赏壹。千萬(wàn)注意不要補(bǔ)同樣的值,最好是隨機(jī)數(shù)衔沼。
(4)coalesce(age,1)
(3)字段值為null若為臟數(shù)據(jù)蝌借,可剔除。
方案二:
若仍保留null值指蚁,判斷條件可以為:
select id,age,name,sex,job from not_eq_temp where (age <> 22 or age is null )
另外:使用不等值!= 或<>需要注意的
在使用不等值:<>比較或過(guò)濾數(shù)據(jù)時(shí)菩佑,需要注意以下多種情況。
先來(lái)看看<>語(yǔ)法格式:
語(yǔ)法: A <> B
針對(duì)所有基本類型凝化,如果表達(dá)式A為NULL稍坯,或者表達(dá)式B為NULL,返回NULL;如果表達(dá)式A與表達(dá)式B不相等瞧哟,則為TRUE混巧;否則為FALSE。
注意:在關(guān)系型數(shù)據(jù)庫(kù)中勤揩,通常SQL的寫法中不等于也可以這樣寫 != 咧党。但在hive中,當(dāng)一個(gè)string類型和int類型在進(jìn)行比較的時(shí)候會(huì)查不出來(lái)結(jié)果陨亡。
數(shù)字和數(shù)字類型:可以用 != 比較傍衡;
帶引號(hào)的數(shù)字和數(shù)字類型:也可以用!= 比較;
帶引號(hào)的數(shù)字和帶引號(hào)數(shù)字類型:還可以用 != 比較负蠕;
字符串和數(shù)字類型:不可以用 != 比較聪舒;
字符串和數(shù)字類型:不可以用 <> 比較;
總而言之虐急,在使用!= 或 <>比較的時(shí)候兩者的字段類型盡量保持一致。