# 索引不会包含有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

MySQL索引对NULL值的处理的更多相关文章

  1. mysql 索引列为Null的走不走索引及null在统计时的问题

    要尽可能地把字段定义为 NOT NULL,即使应用程序无须保存 NULL(没有值),也有许多表包含了可空列(Nullable Column)这仅仅是因为它为默认选项.除非真的要保存 NULL,否则就把 ...

  2. 让索引包含null值的两种方法

    1. 把有NULL值的列与一个常数,或者一个带有not null约束的列一同索引 create index ind_01 on t01(col01,1); 或者 create index ind_01 ...

  3. Mysql 关于处理NULL值的相关函数和操作符

    操作符 <=> NULL-safe equal. This operator performs an equality comparison like the = operator, bu ...

  4. Mysql整数运算NULL值处理注意点

    CleverCode近期在导出报表的时候,在整数做减法的时候,发现整数减去null得到是null.这是一个细节问题,希望大家以后注意. 1 表中的数据 total,used都是整形,同意为空. 2 有 ...

  5. 【转】Oracle索引列NULL值引发执行计划该表的测试示例

    有时开发进行表结构设计,对表字段是否为空过于随意,出现诸如id1=id2,如果允许字段为空,因为Oracle中空值并不等于空值,有可能得到意料之外的结果.除此之外,最关键的是,NULL会影响oracl ...

  6. mysql中的null字段值的处理及大小写问题

    在MySQL中,NULL字段的处理,需要注意,当在处理查询条件中有NULL,很有可能你得到的值不是想要的,因为,在MySQL中,判断NULL值相等(=)或者不等(!=)都会返回false.主要出现在常 ...

  7. MySQL中order by中关于NULL值的排序问题

    MySQL中order by 排序遇到NULL值的问题 MySQL数据库,在order by排序的时候,如果存在NULL值,那么NULL是最小的,ASC正序排序的话,NULL值是在最前面的. 如果我们 ...

  8. MySQL的NULL值处理

    我们已经知道MySQL使用 SQL SELECT 命令及 WHERE 子句来读取数据表中的数据,但是当提供的查询条件字段为 NULL 时,该命令可能就无法正常工作. 为了处理这种情况,MySQL提供了 ...

  9. MySQL NULL 值处理

    MySQL NULL 值处理 我们已经知道MySQL使用 SQL SELECT 命令及 WHERE 子句来读取数据表中的数据,但是当提供的查询条件字段为 NULL 时,该命令可能就无法正常工作. 为了 ...

随机推荐

  1. H - Almost Union-Find

    //带删除操作的并查集 //题意:给你一个1~n的集合,有三种操作 // 1: 把p和q所在的集合合并 //2:把p移到q所在的集合中 //3:返回p所在集合中的元素个数和元素的和 //第二种操作不能 ...

  2. 《挑战30天C++入门极限》图文例解C++类的多重继承与虚拟继承

        图文例解C++类的多重继承与虚拟继承 在过去的学习中,我们始终接触的单个类的继承,但是在现实生活中,一些新事物往往会拥有两个或者两个以上事物的属性,为了解决这个问题,C++引入了多重继承的概念 ...

  3. CSS3字体大小rem属性用法

    PX为单位 在Web页面初期制作中,我们都是使用“px”来设置我们的文本,因为他比较精确和固定. 只要页面某元素设置了px字体大小,其子元素/子孙元素未设置字体大小或设置的字体大小css优先级没父元素 ...

  4. Alpha(2/6)

    队名:無駄無駄 组长博客 作业博客 组员情况 张越洋 过去两天完成了哪些任务 任务分配.进度监督 提交记录(全组共用) 接下来的计划 沟通前后端成员,监督.提醒他们尽快完成各自的进度 还剩下哪些任务 ...

  5. C语言的柔性数组的实现及应用

    c编程的时候数组长度一般都是固定好的,实际上c还能实现变长数组.其实c99中确实是有变长数组的说法,C99中通过允许结构体中的最后一个成员是长度未知的数组实现变长数组,其定义格式如下: typedef ...

  6. dhcp自动分配地址

  7. call 和 apply 方法

    1:每个函数都包含两个非继承而来的方法:call(),apply(). 2:call方法和apply方法作用是一样的. 下边是call的使用例子: window.color = 'red'; docu ...

  8. mongo最大连接数查看

    进入客户端 mongo 输入查看命令 db.serverStatus().connections

  9. HikariCP 连接最快的连接池

    三点原因 1.字节码精简 2.自定义 FastList 代替ArrayList ;避免每次get()调用都要进行range check,避免调用remove()时的从头到尾的扫描: 3.优化代码和拦截 ...

  10. python中requests里.text和.content方法的区别

    requests对象的get和post方法都会返回一个Response对象,这个对象里面存的是服务器返回的所有信息,包括响应头,响应状态码等.其中返回的网页部分会存在.content和.text两个对 ...