下面咱們要聊的是 MySQL 里的 null,在大量的 MySQL 優化文章和書籍里都提到了字段盡可能用NOT NULL,而不是NULL,除非特殊情況。但卻都只給結論不說明原因,猶如雞湯不給勺子一樣,讓不少初學者對這個結論半信半疑或者云里霧里。本文今天就詳細的剖析下使用 Null 的原因,并給出一些不用 Null 的理由。
1、NULL 為什么這么多人用?
NULL是創建數據表時默認的,初級或不知情的或怕麻煩的程序員不會注意這點。
很多人員都以為not null 需要更多空間,其實這不是重點。
重點是很多程序員覺得NULL在開發中不用去判斷插入數據,寫sql語句的時候更方便快捷。
2、是不是以訛傳訛?
MySQL 官網文檔:
NULL columns require additional space in the rowto record whether their values are NULL. For MyISAM tables, each NULL columntakes one bit extra, rounded up to the nearest byte.
create table table_2 (`id` INT (11) NOT NULL,user_name varchar(20) NOT NULL
)create table table_3 (`id` INT (11) NOT NULL,user_name varchar(20)
)insert into table_2 values (4,"zhaoliu_2_1"),(2,"lisi_2_1"),(3,"wangmazi_2_1"),(1,"zhangsan_2"),(2,"lisi_2_2"),(4,"zhaoliu_2_2"),(3,"wangmazi_2_2")insert into table_3 values (1,"zhaoliu_2_1"),(2, null)-- 1、NOT IN子查詢在有NULL值的情況下返回永遠為空結果,查詢容易出錯
select user_name from table_2 where user_name not in (select user_name from table_3 where id!=1)mysql root@10.48.186.32:t_test_zz5431> select user_name from table_2 where user_name not-> in (select user_name from table_3 where id!=1);
+-------------+
| user_name |
|-------------|
+-------------+
0 rows in set
Time: 0.008s
mysql root@10.48.186.32:t_test_zz5431>-- 2、單列索引不存null值,復合索引不存全為null的值,如果列允許為null,可能會得到“不符合預期”的結果集
-- 如果name允許為null,索引不存儲null值,結果集中不會包含這些記錄。所以,請使用not null約束以及默認值。
select * from table_3 where name != 'zhaoliu_2_1'-- 3、如果在兩個字段進行拼接:比如題號+分數,首先要各字段進行非null判斷,否則只要任意一個字段為空都會造成拼接的結果為null。
select CONCAT("1",null) from dual; -- 執行結果為null。-- 4、如果有 Null column 存在的情況下,count(Null column)需要格外注意,null 值不會參與統計。
mysql root@10.48.186.32:t_test_zz5431> select * from table_3;
+------+-------------+
| id | user_name |
|------+-------------|
| 1 | zhaoliu_2_1 |
| 2 | <null> |
| 21 | zhaoliu_2_1 |
| 22 | <null> |
+------+-------------+
4 rows in set
Time: 0.007s
mysql root@10.48.186.32:t_test_zz5431> select count(user_name) from table_3;
+--------------------+
| count(user_name) |
|--------------------|
| 2 |
+--------------------+
1 row in set
Time: 0.007s-- 5、注意 Null 字段的判斷方式, = null 將會得到錯誤的結果。
mysql root@localhost:cygwin> create index IDX_test on table_3 (user_name);
Query OK, 0 rows affected
Time: 0.040s
mysql root@localhost:cygwin> select * from table_3 where user_name is null\G
***************************[ 1. row ]***************************
id | 2
user_name | None1 row in set
Time: 0.002s
mysql root@localhost:cygwin> select * from table_3 where user_name = null\G0 rows in set
Time: 0.002s
mysql root@localhost:cygwin> desc select * from table_3 where user_name = 'zhaoliu_2_1'\G
***************************[ 1. row ]***************************
id | 1
select_type | SIMPLE
table | table_3
type | ref
possible_keys | IDX_test
key | IDX_test
key_len | 23
ref | const
rows | 1
Extra | Using where1 row in set
Time: 0.006s
mysql root@localhost:cygwin> desc select * from table_3 where user_name = null\G
***************************[ 1. row ]***************************
id | 1
select_type | SIMPLE
table | None
type | None
possible_keys | None
key | None
key_len | None
ref | None
rows | None
Extra | Impossible WHERE noticed after reading const tables1 row in set
Time: 0.002s
mysql root@localhost:cygwin> desc select * from table_3 where user_name is null\G
***************************[ 1. row ]***************************
id | 1
select_type | SIMPLE
table | table_3
type | ref
possible_keys | IDX_test
key | IDX_test
key_len | 23
ref | const
rows | 1
Extra | Using where1 row in set
Time: 0.002s
mysql root@localhost:cygwin>
(5)Null 列需要更多的存儲空間:需要一個額外字節作為判斷是否為 NULL 的標志位
alter table table_3 add index idx_user_name (user_name);
alter table table_2 add index idx_user_name (user_name);
explain select * from table_2 where user_name='zhaoliu_2_1';
explain select * from table_3 where user_name='zhaoliu_2_1';