[Hive][COUNT] 使用count后出現null問題排查
問題概述
使用hive進行用戶頻次類數據分組提取時,最終的結果出現了全部為null的記錄,同時也有全為0的記錄,分析原因
v1HQL邏輯
with sup_tab as(
取出用戶所用行為記錄
)
select 用戶id, count( 條件1 ) as cnt_1, count( 條件2 ) as cnt_2 ,...
from sup_tab
where 時間窗口限制
group by 用戶標識
v1存在問題
由於HQL的select后的字段只能是統計字段,以及分組字段,即只能出現count,sum,avg等聚合函數和group by 字段之后的分組字段,所以,若想要使得用戶id(非用戶標識)出現在最終的表中,所以我采用了以下邏輯
v2HQL邏輯
with sup_tab as(
取出用戶所用行為記錄
)
select 用戶id, count( 條件1 ) as cnt_1, count( 條件2 ) as cnt_2 ,...
from (
用戶基本信息
from sup_tab
) id
left join
(
統計量
from sup_tab
where 時間窗口限制
group by 用戶標識
) cnt
on id.用戶標識 = cnt.用戶標識
為什么不使用用戶id進行分組統計
因為用戶id字段存在空值和非法值
問題分析
首先明確三點
- A left join B B中若無A的條目,則全部填充為NULL
- COUNT(NULL) = 0
- select COUNT from where 是對滿足了where后的數據進行操作
問題出在統計頻次時,使用了時間窗口進行過濾,這導致一些用戶的行為全部被過濾掉,所以在最終統計過程中沒有這個人,在left join過程中出現了NULL填充現象
解決方法
方案一:使用 inner join 代替 left join
好處:直接去除沒有數據的用戶,不會出現全NULL的用戶了
壞處:需要shuffle操作,這導致HQL執行速度down
方案二:不使用id表,直接從cnt表中獲得用戶id
v1方法: 將用戶id加在group by 后
是否有潛在風險?
首先明確以下兩點邏輯
- HQL會根據group by后面的字段進行分組統計 如果有ABC三個字段,如果A中有a個不重復字段,B中有b個不重復字段,C中有c個不重復字段,在不考慮where過濾的情況下,那么最終會出現abc個數據記錄
- 想輸出用戶id時,一個用戶標識字段中對應一個用戶id(可能是空值或非法值,但同一個用戶標識字段內,對應的用戶id都相同,是空值時均為空值,是非法值時均為非法值)
所以使用v1方法,理論上是存在風險的,但是實際在操作過程中,是可以規避風險的
好處:回避join導致的shuffle
壞處,group by 后字段數量增加,效率可能降低? (這一點是我有所顧慮的,沒有去實踐)
v2方法,使用collect_set(字段)[0]方法
collect_set(字段)可以將數據記錄,然后返回一個去重后的列表,常用做列轉行數據,如果不想對數據進行去重,那么使用collect_list(字段)即可,只需要取這個去重列表的第0個數據就可以達到獲得用戶ID的目的,根據v1方法的分析,在使用collect_set進行突破group by限制時,其中只會有一個數據,如果有一個以上時,說明一個用戶識別字段對應多個用戶ID,需要進行排查。
寫在最后
由於公司數據需要保密,所以基本上只能描述問題和思路,具體問題不能直觀貼出來,SQL的相關邏輯還是要多進行測試,但直接拿大數據表來測試邏輯又不合適,我並沒有學習過MySQL這種SQL類編程,所以,找了一個在線練習的網站,希望有相同問題的朋友可以在一些在線網站測試一些簡單邏輯,就不用裝SQL到個人PC上了。
SQL邏輯練習
https://sqlzoo.net/wiki/Using_Null/zh