NULL在oracle和mysql索引上的区别
一、问题
oracle的btree索引不存储NULL值,所以用is null或is not null都不会用到索引范围扫描,但是在mysql中也是这样吗?
二、实验
先看看NULL在oracle(11g)中的情况
准备测试数据
SQL> create table t1 as select * from dba_objects;
SQL> update t1 set object_id = null where object_id > 17840;
SQL> update t1 set data_object_id = null where data_object_id > 60;
SQL> commit;
SQL> create index idx1_id on t1(object_id);
SQL> create index idx2_data on t1(data_object_id);
搜集统计信息
SQL> begin
dbms_stats.gather_table_stats(ownname => 'SCOTT',
tabname => 'T1',
estimate_percent => 100,
cascade => true,
method_opt => 'for all indexed columns size auto',
no_invalidate => false,
degree => 4);
end;
/
查看数据分布
SQL> select count(*) "总行数",
2 count(distinct object_id) "object_id非空不同值",
3 count(decode(object_id,null,1,null)) "object_id空值总数",
4 count(distinct data_object_id) "data_object_id非空不同值",
5 count(decode(data_object_id,null,1,null)) "data_object_id空值总数"
6 from t1;
总行数 object_id非空不同值 object_id空值总数 data_object_id非空不同值 data_object_id空值总数
---------- ------------------- ----------------- ------------------------ ----------------------
13582 13578 4 47 13510
执行sql,并查看执行计划
第1条sql:is null返回行数少
SQL> select * from t1 where object_id is null;
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 50 (100)| |
|* 1 | TABLE ACCESS FULL| T1 | 4 | 352 | 50 (0)| 00:00:01 |
--------------------------------------------------------------------------
第2条sql:is not null返回行数多
SQL> select * from t1 where object_id is not null;
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 50 (100)| |
|* 1 | TABLE ACCESS FULL| T1 | 13578 | 1166K| 50 (0)| 00:00:01 |
--------------------------------------------------------------------------
第3条sql:is null返回行数多
SQL> select * from t1 where data_object_id is null;
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 50 (100)| |
|* 1 | TABLE ACCESS FULL| T1 | 13510 | 1161K| 50 (0)| 00:00:01 |
--------------------------------------------------------------------------
第4条sql:is not null返回行数少
SQL> select * from t1 where data_object_id is not null;
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 7 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| T1 | 72 | 6336 | 7 (0)| 00:00:01 |
|* 2 | INDEX FULL SCAN | IDX2_DATA | 72 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
可以看到第1条和第3条sql不会用到索引,这是由于oracle的btree索引并不存储NULL,所以用is null作为条件在索引中找不到任何结果,只能全表扫。
第2条sql也没有用到索引,因为返回的行数多。第4条sql用到了索引,但用的是索引全扫描,原理其实还是由于索引不存储NULL,is not null正好跟索引特性相同。
接下来我们看看在mysql(8.0)中又会是什么情形,通过工具把上面的表导入到mysql中
更新t1表的统计信息
analyze table t1;
查看执行计划
第5条sql:is null返回行数少
(scott@localhost)[hello]> explain select * from t1 where object_id is null;
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-----------------------+
| 1 | SIMPLE | t1 | NULL | ref | IDX1_ID | IDX1_ID | 5 | const | 4 | 100.00 | Using index condition |
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-----------------------+
第6条sql:is not null返回行数多
(scott@localhost)[hello]> explain select * from t1 where object_id is not null;
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| 1 | SIMPLE | t1 | NULL | ALL | IDX1_ID | NULL | NULL | NULL | 13541 | 50.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
第7条sql:is null返回行数多
(scott@localhost)[hello]> explain select * from t1 where data_object_id is null;
+----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-----------------------+
| 1 | SIMPLE | t1 | NULL | ref | IDX2_DATA | IDX2_DATA | 5 | const | 6770 | 100.00 | Using index condition |
+----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-----------------------+
第8条sql:is not null返回行数少
(scott@localhost)[hello]> explain select * from t1 where data_object_id is not null;
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | t1 | NULL | range | IDX2_DATA | IDX2_DATA | 5 | NULL | 72 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
可以看到在mysql中,is not null会根据返回的行数来决定用不用索引,返回行数多不用索引,返回行数少用索引,这一点跟oracle相同。但is null都会用到索引,不管你返回的行数是多少,这点的确是出乎我的意料。既然is null用到索引,那么难道是mysql的btree索引中包含NULL值?
查看索引的信息
(scott@localhost)[hello]> select * from mysql.innodb_index_stats where database_name='hello' and table_name='t1' and index_name in ('IDX1_ID', 'IDX2_DATA');

从索引的统计信息中可以看到,mysql认为t1表的OBJECT_ID,DATA_OBJECT_ID的不同值分别是13579,48。而前面我们知道object_id非空不同值和data_object_id非空不同值分别为13578和47。两者都相差1,那也就是说索引的确是含NULL值。
三、总结
- mysql中btree索引含NULL,这点跟oracle不一样。
- mysql中用is null都会用到索引,不管返回的行数多少,我认为这是一个bug。
水平有限,如果有误,恳请大家指正!
NULL在oracle和mysql索引上的区别的更多相关文章
- Oracle和MySql的分页查询区别和PL/SQL的基本概念
Oracle和MySql的分页查询区别: Oracle的分析查询,之前Oracle的分页是使用伪列 ROWNUM 结合子查询实现,mysql的分页更简单,直接使用 LIMIT 关键字就可以实现 ...
- 图解MySQL索引(上)—MySQL有中“8种”索引?
关于MySQL索引相关的内容,一直是一个让人头疼的问题,尤其是对于初学者来说.笔者曾在很长一段时间内深陷其中,无法分清"覆盖索引,辅助索引,唯一索引,Hash索引,B-Tree索引--&qu ...
- Oracle与MySQL的几点区别
Oracle数据库与MySQL数据库的区别是本文我们主要介绍的内容,希望能够对您有所帮助. 1.组函数用法规则 mysql中组函数在select语句中可以随意使用,但在oracle中如果查询语句中有组 ...
- 深入浅出之mysql索引--上
当着小萌新之际,最近工作中遇到了mysql优化的相关问题,然后既然提到了优化,很多像我这样的小萌新不容置喙,肯定张口就是 建立索引 之类的. 那么说到底,索引到底是什么,它是怎么工作的?接下来就让我和 ...
- 深入理解MySQL索引(上)
简单来说,索引的出现就是为了提高数据查询的效率,就像字典的目录一样.如果你想快速找一个不认识的字,在不借助目录的情况下,那我估计你的找好长时间.索引其实就相当于目录. 几种常见的索引模型 索引的出现是 ...
- Oracle与MySQL的SQL语句区别
2 表 2.1 创建表(同) create table tableName( columnName1 int, columnName2 int ) 2.2 删除表(异) MySQL: drop tab ...
- Oracle中和mysql中函数的区别
oracle --> mysqlto_char(sysdate,'yyyy-mm-dd')-->date_format(s ...
- Oracle与MySQL的区别
1. Oracle是大型数据库而Mysql是中小型数据库,Oracle市场占有率达40%,Mysql只有20%左右,同时Mysql是开源的而Oracle价格非常高. 2. Oracle支持大并发,大访 ...
- SQL Server 和 Oracle 以及 MySQL 数据库
推荐:https://www.zhihu.com/question/19866767 三者是目前市场占有率最高(依安装量而非收入)的关系数据库,而且很有代表性.排行第四的DB2(属IBM公司),与Or ...
随机推荐
- miRNA 基本知识
miRNA MicroRNA (miRNA) 是一类内生的.长度约为20-24个核苷酸的小 RNA,其在细胞内具有多种重要的调节作用.每个 miRNA 可以有多个靶基因的表达,而几个 miRNA 也 ...
- 56-Remove Linked List Elements
Remove Linked List Elements My Submissions QuestionEditorial Solution Total Accepted: 61924 Total Su ...
- Markdown-写作必备
Markdown--入门指南 导语: Markdown 是一种轻量级的「标记语言」,它的优点很多,目前也被越来越多的写作爱好者,撰稿者广泛使用.看到这里请不要被「标记」.「语言」所迷惑,Markdow ...
- 2.MaxSubArray-Leetcode
题目:最大连续子序列和 思路:动态规划 状态转移方程 f[j]=max{f[j-1]+s[j],s[j]}, 其中1<=j<=n target = max{f[j]}, 其中1<=j ...
- CMakeLists.txt添加多个源代码
coos2d-x 3.17.2 C++工程,安卓编译使用CMake,按照模板给的写法,只能一个一个源文件添加:如果需要添加大量的C++源代码,这种方式肯定不可取:原来的写法: 1 list(APPEN ...
- LetNet、Alex、VggNet分析及其pytorch实现
简单分析一下主流的几种神经网络 LeNet LetNet作为卷积神经网络中的HelloWorld,它的结构及其的简单,1998年由LeCun提出 基本过程: 可以看到LeNet-5跟现有的conv-& ...
- c#年份筛选
年份: <script type="text/javascript" src="http://www.shicishu.com/down/WdatePicker.j ...
- Js数组内对象去重
let person = [ {id: 0, name: "小明"}, {id: 1, name: "小张"}, {id: 2, name: "小李& ...
- java加密方式
加密,是以某种特殊的算法改变原有的信息数据,使得未授权的用户即使获得了已加密的信息,但因不知解密的方法,仍然无法了解信息的内容.大体上分为双向加密和单向加密,而双向加密又分为对称加密和非对称加密(有些 ...
- BigDecimal 计算注意事项
BigDecimal 在进行除法运算(divide)时一定要注意:如果被除数为变量,一定要指定精度 和 舍入模式,否则会报:Non-terminating decimal expansion; no ...