平时,我们都是通过order by 来强制性要求排序的,那如果我们不加的话,MySQL根据什么来排序呢,下面来分析下。
MYSQL的官网说有如下两句话:
1、对于MYISAM引擎
SELECT * FROM tbl — this will do a “table scan”. If the table has never had any DELETEs/REPLACEs/UPDATEs, the records will happen to be in the insertion order, hence what you observed.
大致意思是说,在查询表的时候,会进行表扫描,如果表从未进行过任何删除/替换/更新,你将会观察到这些记录将按照插入顺序排列。
也就是说,我们查询会来的数据是按插入顺序排序的,并且会进行表扫描。上面还提到说未进行任何删除/替换/更新才会观察到是按插入顺序排序,如果进行了操作结果就不一定了。从这里我们可以大胆的推测,MYISAM引擎应该是按照存储的顺序排序的,如果我们连续插入几条记录,再删掉之前的一条记录,那么此时那一条记录所占的位置就空出来了,如果再插入一条记录,就会直接占据那个位置。
我们也可以合理的推测,这样设计的原因可能是因为MyISAM是非聚集索引,对于B+树来说,非聚集索引,叶子节点只会存储指向数据页数据行的逻辑指针,并不存放位置这个原理来设计的。
2、对于INNODB引擎
If you had done the same statement with an InnoDB table, they would have been delivered in PRIMARY KEY order, not INSERT order. Again, this is an artifact of the underlying implementation, not something to depend on.
大概意思是说,对于INNODB引擎在没有order by的情况下,会根据主键从小到大来排序的。这是底层实现的一个工件,不是可以依赖的东西。
也就是说,这个跟MYISAM不同,INNODB是根据主键来排序的,如果我们删除之前的一条数据,再插入一条,排序顺序没有变化,还是会根据主键从小到大。但是后面有一句这是底层实现的一个工件,不是可以依赖的东西表明我们不可以依赖这个来排序,还是需要根据 order by来。
我这边可以推测下,为啥是根据ID来而不是存储顺序来,是因为INNODB索引引擎的实现是聚集缩索引数据和叶子结点是跟在一起的,所以删除就是删除了,不会有空位指这存储地址。
具体索引的原理可以参考:MySQL索引原理解析
3、测试
我们这里对上面的两种情况进行测试,看MYISAM引擎是否真的按存储地址顺序排序,INNODB引擎是否真的按注解ID排序。
新建两个表,分别使用不同的引擎。
CREATE TABLE testINNODB(
id INTEGERnotnullprimary key auto_increment COMMENT'自增的ID',
name VARCHAR(100)notnullCOMMENT'姓名'
)ENGINE=INNODB;
CREATE TABLE testMYISAM(
id INTEGERnotnullprimary key auto_increment COMMENT'自增的ID',
name VARCHAR(100)notnullCOMMENT'姓名'
)ENGINE=MYISAM;
插入入下数据
INSERTintotestINNODB(name)VALUES('A');
INSERTintotestINNODB(name)VALUES('B');
INSERTintotestINNODB(name)VALUES('C');
INSERTintotestINNODB(name)VALUES('D');
INSERTintotestINNODB(name)VALUES('E');
INSERTintotestMYISAM(name)VALUES('A');
INSERTintotestMYISAM(name)VALUES('B');
INSERTintotestMYISAM(name)VALUES('C');
INSERTintotestMYISAM(name)VALUES('D');
INSERTintotestMYISAM(name)VALUES('E');
现在查询,两个表的顺序都是A B C D E.
删除第三条记录。
deletefromtestINNODBwhereid=3;
deletefromtestMYISAMwhereid=3;
此时删除完后,按上面的分析,MYISAM引擎那个第三条记录的存储空间空余出来了,再插入如下两条记录。
INSERTintotestINNODB(name)VALUES('C1');
INSERTintotestMYISAM(name)VALUES('C1');
执行查询操作
SELECT*FROM testINNODB;
SELECT*FROM testMYISAM;
如果testINNODB结果是 A B D E C1,testMYISAM的结果是A B C1 D E就表明上面的结论正确。
查看结果如下
结论正确。
4、oracle 无order by , 默认排序问题
这里顺便提一下;oracle 无order by 是怎么默认排序的.
oracle数据库是没有默认排序的 ,要排序必须加上order by ,因为oracle是按块进行读取数据的,如果数据按顺序存储,则可能使读取出来的数据是按顺序的,给用户误解为默认排序oracle没有进行任何排序操作,rowid表示的是数据存放的数据块内部地址,如果没有要求排序,oracle会顺序的从数据块中读取符合条件的数据返回到客户端,不过看起来好像是按照rowid排序似的.
如果数据量足够大,即使相同的语句,都有可能不同的结果。