MySQL索引對NULL值的處理


 

# 索引不會包含有NULL值的列
只要列中包含有NULL值都將不會被包含在索引中,復合索引中只要有一列含有NULL值,那么這一列對於此復合索引就是無效的。所以我們在數據庫設計時不要讓字段的默認值為NULL。

在很多庫表設計規范、某某軍規的文章中,是不是經常會看到類似這樣的內容。小編也經常看到這樣的內容,並且在編寫規范的時候,准備也把這一條加進去。但在按部就班之余,小編抽空驗證了一下,發現事實卻並非如此!

小編使用的MySQL版本是社區版 5.7.21

 

新建測試表 t1,插入不含NULL值得100行數據,然后插入1行帶NULL的數據 insert into t1(id) values(101); 表中有主鍵id,索引a

CREATE TABLE `t1` (
  `id` int(11) NOT NULL,
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `a` (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

 

測試1,包含NULL單列索引的查詢,可以看到即使是查找 IS NULL的行,也是可以用上索引的

測試1:

desc
select * from t1 where a > 82; +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+ | 1 | SIMPLE | t1 | NULL | range | a | a | 5 | NULL | 18 | 100.00 | Using index condition | +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+ desc select * from t1 where a is NULL; +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-----------------------+ | 1 | SIMPLE | t1 | NULL | ref | a | a | 5 | const | 1 | 100.00 | Using index condition | +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-----------------------+ desc select * from t1 where a = 20 or a is null; +----+-------------+-------+------------+-------------+---------------+--------+---------+-------+------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------------+---------------+--------+---------+-------+------+----------+--------------------------+ | 1 | SIMPLE | t1 | NULL | ref_or_null | idx_ab | idx_ab | 5 | const | 2 | 100.00 | Using where; Using index | +----+-------------+-------+------------+-------------+---------------+--------+---------+-------+------+----------+--------------------------+

注意對 NULL 值的檢索只能使用 is null / is not null / <=>,不能使用=,<,>這樣的運算符(mysql中可以用a <=> NULL 表示查找 a is NULL'的行)

 

測試2,包含NULL復合索引的查詢,首先加一個復合索引 alter table t1 drop index a,add index idx_ab(a,b); 可以看到不管是指定 a is null ,或者指定 b is null ,都可以利用上索引 idx_ab(key_len 可以看出)

測試2:

desc
select * from t1 where a=50 and b>20; +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------+ | 1 | SIMPLE | t1 | NULL | range | idx_ab | idx_ab | 10 | NULL | 1 | 100.00 | Using where; Using index | +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------+ desc select * from t1 where a=50 and b is null; +----+-------------+-------+------------+------+---------------+--------+---------+-------------+------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+--------+---------+-------------+------+----------+--------------------------+ | 1 | SIMPLE | t1 | NULL | ref | idx_ab | idx_ab | 10 | const,const | 1 | 100.00 | Using where; Using index | +----+-------------+-------+------------+------+---------------+--------+---------+-------------+------+----------+--------------------------+ desc select * from t1 where a is null and b>20; +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------+ | 1 | SIMPLE | t1 | NULL | range | idx_ab | idx_ab | 10 | NULL | 1 | 100.00 | Using where; Using index | +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------+ desc select * from t1 where a is null and b is null; +----+-------------+-------+------------+------+---------------+--------+---------+-------------+------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+--------+---------+-------------+------+----------+--------------------------+ | 1 | SIMPLE | t1 | NULL | ref | idx_ab | idx_ab | 10 | const,const | 1 | 100.00 | Using where; Using index | +----+-------------+-------+------------+------+---------------+--------+---------+-------------+------+----------+--------------------------+

 

由此,只要列中包含有NULL值都將不會被包含在索引中,復合索引中只要有一列含有NULL值,那么這一列對於此復合索引就是無效的。所以我們在數據庫設計時不要讓字段的默認值為NULL。這句的前半句是不對的(可參考官網說明: https://dev.mysql.com/doc/refman/5.7/en/is-null-optimization.html),但是后半句的結論確是可以采納的。

雖然MySQL可以在含有null的列上使用索引,但不代表null和其他數據在索引中是一樣的。不建議列上允許為空,最好限制 not null ,並設置一個默認值,比如0和''空字符串等,如果是datetime類型,可以設置成'1970-01-01 00:00:00'這樣的值。對MySQL來說,null 是一個特殊的值,Conceptually, NULL means “a missing unknown value” and it is treated somewhat differently from other values。 對null做算術運算的結果都是null,count時不會包括null行,null 比空字符串需要更多的存儲空間等。

 

附:上面說到可用通過 key_len 看出使用了索引列的個數,a,b 都是 int 類型,4 byte,為什么 key_len 是 5 byte 和 10 byte 呢?是因為如果索引列定義時允許NULL,其key_len還需要再加 1 bytes. 參考好友王的文章,可以移步我們的站點查看詳情: http://www.fordba.com/spend-10-min-to-understand-how-mysql-use-index.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM