步骤1:
我正在创建一个简单的表格。
CREATE TABLE `indexs`.`table_one` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(45) NULL,
PRIMARY KEY (`id`));
第2步:
我在该表中插入了两次内容。
insert into table_one (name) values ("B");
insert into table_one (name) values ("A");
步骤3:
我进行选择,得到一个表,其中的记录按 id 排序。
SELECT * FROM table_one;
这是预期的结果,因为在mysql中主键是聚集索引,因此数据将通过它进行物理排序。
现在我不明白的部分。
第四步:
我正在名称列上创建索引。
CREATE INDEX index_name ON table_one(name)
我再次重复步骤3,但得到了不同的结果。这些行现在根据名称列进行排序。
为什么会发生这种情况?为什么表中行的顺序会根据名称列上的新索引而变化,因为据我了解,在mysql中,主键是唯一的聚集索引,所有额外创建的索引都是辅助索引。
我进行选择,得到一个表,其中的记录按 id 排序。 [...]这是预期的结果,因为在mysql中主键是聚集索引,因此数据将通过它进行物理排序。
这里对一个概念有一些误解。
表行没有固有的顺序:它们代表无序的行集。虽然聚集索引强制存储中数据的物理顺序,但它不能保证
select
查询返回行的顺序。
如果您希望查询结果排序,请使用
order by
子句。如果没有这样的子句,排序或行是未定义的:数据库可以自由地按照它喜欢的任何顺序返回结果,并且不能保证结果在连续执行同一查询时保持一致。
select * from table_one order by id;
select * from table_one order by name;
(GMB 解释最多)
为什么会发生这种情况?为什么表中的行顺序会根据名称列上的新索引而变化
使用
EXPLAIN SELECT ...
——它可能会提供我要建议的线索。
您添加了
INDEX(name)
。在 InnoDB 中,PRIMARY KEY
列被附加到每个二级索引的末尾。所以它实际上是一个按 (name,id)
排序的 BTree,并且只包含那些列。
现在,优化器可以自由地从索引中获取数据,因为它拥有您要求的所有内容(id 和名称)。 (这个指标称为“覆盖”。)
由于您没有指定
ORDER BY
,因此结果集排序有效(请参阅 GMB 的讨论)。
故事寓意:如果您想要订购,请指定
ORDER BY
。 (如果优化器能够了解如何在不进行额外排序的情况下提供排序结果,那么它就足够聪明,可以“不做额外的工作”。
进一步实验:向表中添加另一列,但不更改索引。现在您会发现
SELECT * FROM t
的顺序与 SELECT id, name FROM t
不同。我想我已经给了你足够的线索来预测这种差异,如果没有,请询问。