注意!SQL中的NULL
大家好,我是寶器!
越發覺得取數之前的“預處理”非常重要,其中最核心的一點是檢查資料的準確性。大的方向有兩種,其一,確認資料本身無錯亂,其二,保障取數業務邏輯準確。
第一種比較繁瑣、耗時,但是好處理(習慣對結果做一下統計值分佈可以減少很多異常)。第二種不是SQL執行過程中報錯,而是返回的結果和你需要的不太一樣。今天主要聊一下取數分析中容易忽略的點,尤其是SQL中的NULL值。
1、空值JOIN時導致資料丟失
建立案例資料表
CREATETABLEIFNOTEXISTStmp_test_3 ( id_1INT, col_1VARCHAR(255), col_2VARCHAR(255) ); CREATETABLEIFNOTEXISTStmp_test_4 ( id_2INT, col_3VARCHAR(255), col_4VARCHAR(255) ); INSERTINTOtmp_test_3(id_1,col_1,col_2)VALUES(1,'a',null); INSERTINTOtmp_test_3(id_1,col_1,col_2)VALUES(2,'b','join_key_1'); INSERTINTOtmp_test_3(id_1,col_1,col_2)VALUES(3,'c','join_key_2'); INSERTINTOtmp_test_4(id_2,col_3,col_4)VALUES(1,'a',null); INSERTINTOtmp_test_4(id_2,col_3,col_4)VALUES(2,'b','join_key_1'); INSERTINTOtmp_test_4(id_2,col_3,col_4)VALUES(3,'c','join_key_2');
檢視下tmp_test_3、tmp_test_4兩個案例表的資料(分佈是類似的)
現在有個業務,部分資料存在tmp_test_3表,有一些存在tmp_test_4表,假設要得到兩個表中的資料,需要這兩個表按col_2、col_4列JOIN連線。
SELECT
*
FROM
tmp_test_3t_a
LeftJOIN
tmp_test_4t_b
on
t_a.col_2=t_b.col_4;
執行一下上面的語句,會得到什麼結果。
結果顯示是col_2和col_4為空的資料是丟失了的。
為什麼?
直接說原因:在tmp_test_3和tmp_test_4表中用於join的列存在NULL值,而NULL和任何值做比較都是返回的NULL(即不能對NULL進行!=、=、>、<等判斷,返回是NULL)。
Coalesce真香函式,將空值替換成一個預設值。
SELECT
*
FROM
tmp_test_3t_a
JOIN
tmp_test_4t_b
on
COALESCE(t_a.col_2,'aaa')=COALESCE(t_b.col_4,'aaa')
這樣就可以把tmp_test_3中包含NULL的資料記錄和tmp_test_4表中的NULL資料記錄JOIN起來。但是這裡有個小問題是他會把這些NULL記錄全部匹配,所以實際應用中可以按照業務需求來做取捨。
2、聚合運算時遇到NULL值
以下是教導主任的302班學生數學成績表,對應了學生名字和成績。
CREATETABLEIFNOTEXISTStmp_score_baoqi_1 ( col_nameVARCHAR(255), col_coreint ); INSERTINTOtmp_score_baoqi_1(col_name,col_core)VALUES('a',null); INSERTINTOtmp_score_baoqi_1(col_name,col_core)VALUES('b',86); INSERTINTOtmp_score_baoqi_1(col_name,col_core)VALUES('c',78); INSERTINTOtmp_score_baoqi_1(col_name,col_core)VALUES('d',65);
你驗證資料的時候發現有學生a的成績是空的(沒參加考試),心裡美滋滋的預處理並且開始執行如下指令碼。
SELECT
avg(IFNULL(col_core,0))
FROM
tmp_score_baoqi_1
--返回57.2500
結果返回:這學期教導主任的302班學生數學平均成績是57.25分,四捨五入為58分,不及格。
很好,執行結果也出來了,也不報錯,但是教導主任卻生氣了,質疑怎麼可能他的班上學生數學成績不及格,需要你核查。
經過排查你發現,原來你做預處理的時候把沒參加考試的學生a缺少的數學成績也算在內,用數值0代替NULL,嚴重影響了最終成績。
這個小例子想說明的就是做聚合運算時要注意NULL值,一定要清楚count、sum、avg函式對NULL的處理:
avg:
SELECT
avg(col_core),avg(IFNULL(col_core,0))
FROM
tmp_score_baoqi_1
--返回76.33、57.2500
count:
SELECT
count(1),count(*),count(col_core)
FROM
tmp_score_baoqi_1
--返回4、4、3
sum:可以對單個列求和,也可以對多個列運算後求和忽略NULL值,且當對多個列運算求和時,如果運算的列中任意一列的值為NULL,則忽略這行的記錄。
補充一條,DISTINCT、ORDER BY、GROUP BY 遇到NULL值視為相等,較好理解,不做資料測試。
請務必注意細節,大概率能決定成敗,本節完。
·················END·················
歡迎長按掃碼關注「資料管道」