数据库联合索引+空值的索引使用问题
昨天在QQ群里讨论一个SQL优化的问题,语句大致如下:
select A,min(B) from table group by A;
--A,B都没有not null约束,A列无空值,B列有空值。
--存在复合索引IX_TEST(A,B)
于是手动测试,先看Oracle,环境采用Oracle自带的scott用户下的emp表。
1.首先查看如下语句的执行计划(此时表只有主键索引):
2.添加IX_TEST(deptno,comm)后查看执行计划:
发现依然是全表扫描。
3.为deptno列添加非空约束后再次查看执行计划:
SQL Server的相关测试流程基本一致。
Tom在《Expert one on Oracle》中说:Oracle数据库中,除位图索引和聚簇索引外,单列Btree索引节点是不存储空值的。联合索引只要有一个列不为空,Btree节点就会存储索引键值。
在本例中我们创建了(deptno,comm)的联合索引,如果deptno没有非空约束优化器选择全表扫描,如果有非空约束优化器选择索引全扫描。说明添加非空约束后Oracle认为所有的记录都已经被包含在了索引中因此无需全表扫描。
因此在Oracle中一定要为联合索引的首列设置非空约束。
4.MySQL测试
在MySQL中这个问题稍微复杂一点,因为MySQL的NULL值和空字符是有区别的,本文只测试innodb存储引擎(MySQL5.7.22版本)。
首先把SCOTT的数据从oracle完全搞到MySQL,除了EMP表外其他3个表的使用navicat即可同步,EMP表由于HIREDATE的日期类型,需要先自己在MySQL端创建表然后使用PLSQL Developer导出SQL插入语句然后将其在MySQL端执行,如下是当前的EMP表数据:
创建索引后结果如下:
可见在Mysql Innodb中无论复合索引首列是否存在非空约束,都会使用索引,这点与Oracle不同。
那么innodb中如果索引中存在NULL值,还会使用索引吗?继续测试:
可以看到存在NULL值,innodb也会走索引,那么空字符呢?
继续测试发现无论字符类型的列中存储的是NULL还是空字符''都是会走索引的。
在3大关系型数据库中,B+tree索引结构最为明了的反而是SQL Server,从dump出来的索引页我们可以看到SQL Server的Btree索引是存储null值的,即便全部都是空也会存储空值+主键书签。而且以上涉及的SQL全部会使用到联合索引,这点不但方便使用而且也很容易想通。
更多更详细的测试有待继续补充。
最后:Oracle数据库列能添加非空约束的一定要添加,MySQL、SQLServer中可能没这么严格。