之前ElasticSearch系列文章中提到了如何處理空值,若為Null則會直接報錯,因為在ElasticSearch中當字段值為null時、空數組、null值數組時,會將其視為該字段沒有值,最終還是需要使用exists或者null_value來處理空值
大多數ElasticSearch的數據都來自于各類數據庫,這里暫且只針對于MySQL,各個開源軟件中都默認兼容各種Null值,空數組等等
若從根源上截斷就可以省很多事,直到現在很多開發小伙伴還是堅韌不拔的給字段的默認值還是Null
本期就來聊一聊為什么不建議給字段的默認值設置為Null
本期環境為:MySQL8.0.26
null
一、案例數據
創建表user
CREATE TABLE `user` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`age` tinyint(4) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
添加數據,共計10條數據,有兩條數據的name值為Null
INSERT INTO `user` (`name`, `age`) VALUES ('kaka', 26);
INSERT INTO `user` (`name`, `age`) VALUES ('niuniu', 26);
INSERT INTO `user` (`name`, `age`) VALUES ('yangyang', 26);
INSERT INTO `user` (`name`, `age`) VALUES ('dandan', 26);
INSERT INTO `user` (`name`, `age`) VALUES ('liuliu', 26);
INSERT INTO `user` (`name`, `age`) VALUES ('yanyan', 26);
INSERT INTO `user` (`name`, `age`) VALUES ('leilie', 26);
INSERT INTO `user` (`name`, `age`) VALUES ('yao', 26);
INSERT INTO `user` (`name`, `age`) VALUES (NULL, 26);
INSERT INTO `user` (`name`, `age`) VALUES (NULL, 26);
一、count數據丟失
在這期 MySQL統計總數就用count,別花里胡哨的《死磕MySQL系列 十》 文章中,已經對count的使用說的非常明白了。
那借著這個案例,來分析一下為什么數據會丟失,先看結果
select count(*) as num1 ,count(name) as num2 from user;
使用count字段名時出現了數據丟失,很明顯是因為主鍵ID9、10這兩條記錄的name值為空造成的。
為什么會出現這種情況?
當count除了主鍵字段外,會有兩種情況:
一種是字段為null,執行時,判斷到有可能是null,但還要把值取出來再判斷下,不是null的進行累加
另一種是字段為not null,執行時,逐行從記錄里邊讀出這個字段,判斷不是null,才進行累加
此時,咱們遇到的問題是name字段的值存在了null值,所以會走第一種情況,不進行統計null值
為什么建議大家都使用count(*)?
MySQL對于count做了專門的優化,跟字段不同的是并不是把所有帶了*的值取出來,而是指定了count(*)肯定不是null,只需要按行累加即可
MySQL團隊對count(*)做了什么優化?
MySQL系列文章至今已經更新了第十八期了,你有沒有猜到原因呢?
現在你應該知道主鍵索引結構中葉子節點存儲的是整行數據,而普通索引葉子節點存儲的是主鍵ID
那對于普通索引來說肯定會比主鍵索引小,因為對于MySQL來說,不管遍歷哪個索引結果都一樣,所以優化器會主動去找到那顆最小的樹進行遍歷。
在邏輯正確的前提下,盡量減少訪問數據量,是數據庫系統設計通用法則之一。
最后給大家留一個問題,為什么Innodb存儲引擎不跟Myisam存儲一樣存儲一個count值呢?
如果不知道的話,可以看上文提到的count文章
二、為distinct打抱不平
在開發工作中使用Distinct進行去重的場景十分的少,大多數情況都是使用group by完成的
select distinct name from user;
可以看到此時的數據依然是正確的,對Null值做了去重的操作
為什么要說這個,因為咔咔在其它的平臺上看到過有人這么使用count(distinct name,mobile),然后說是統計出來的數據不準確。
這種用法依然是count(字段)的用法,distinct本身是會對Null進行去重,去重后依然是需要判斷name的值不為null時,才會進行累計。
所以,不要把鍋甩給distinct
三、使用表達式數據丟失
在一些值為null時,使用表達式會造成數據的不一致,接下來一起看下
select * from user where name != 'kaka';
表達式造成數據丟失
這跟我們的預期結果不大一致,預期是想返回id2~10的數據
當然,這個問題也不是無解,MySQL同樣也提供了方法
要解決這個問題,只能再加一個條件就是把字段值為null的再單獨處理一下
isnull
四、空指針問題
如果一個列存在null值,使用MySQL的聚合函數后返回結果是null,而并非是0,就會造成程序執行時的指針異常
CREATE TABLE user_order (
id INT PRIMARY KEY auto_increment,
num int
) ENGINE='innodb';
insert into user_order(num) values(3),(6),(6),(NULL);
創建用戶訂單數量表,并插入4條數據,接下來演示一下產生的問題
select sum(num) from goods where id>4;
聚合函數產生的問題
可以看到當字段為null時,使用聚合函數返回值就是null,并非是0,那么這個問題要怎么處理呢?
同樣MySQL也給大家提供了對應函數,就是ifnull
select ifnull(sum(num), 0) from goods where id>4;
在這里插入圖片描述
五、這是在難為誰?
當一個字段的值存在null值,若要進行null值查詢時,必須要使用isnull或者ifnull進行匹配查詢,又或者使用is null,is not null。
而常用的表達式就不能再進行使用了,有工作經驗的還好的,要是新人的話會很難受。
接下來看幾個新人經常犯的錯誤
錯誤一
對存在null值的字段使用表達式進行過濾,正確用法應該是is null 或者 is not null
select * from user where name<>null;
在這里插入圖片描述
錯誤二
依然是使用表達式,同樣可以使用isnull
在這里插入圖片描述
六、總結
說了這么多也都感覺到了字段設置為null的麻煩之處,不過幸好的是MySQL對使用is null、isnull()等依然可以使用上索引。
咔咔目前所在的公司存在大量字段默認值就是null,于是代碼中就大量存儲ifnull、is null、is not null等代碼。
一般字段數值類型的默認值就給成0,字符串的給個空也行,千萬不要給null了哈!
文章來自
https://www.cnblogs.com/fkaka/p/16227471.html