MySQL在有索引列情况下 和 无索引情况下 select *的输出结果顺序

本文探讨了MySQL在有索引和无索引情况下执行SELECT *时的输出结果排序规律,并介绍了如何利用FORCE INDEX来指定排序方式。

本文章讨论 MySQL在有索引列情况下 和 无索引情况下 select *的输出结果顺序。


1:在有索引列情况下。

创建一个表格,一个是主键列,一个是索引列。然后插入一批数据,调用select * from test_b,可以发现输出结果并没有按照Id有序,而是按照Type有序。

如果希望按照Id有序,可以使用force index (primary)这一hint语句。

mysql> CREATE TABLE `test_b` (
    ->   `Id` int(11) NOT NULL,
    ->   `Type` int(11) DEFAULT NULL,
    ->   PRIMARY KEY (`Id`),
    ->   KEY `IDX_Type` (`Type`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.20 sec)

mysql> insert into test_b values(1,1),(2,6),(3,2),(7,3),(4,1);
Query OK, 5 rows affected (0.09 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> select * from test_b;
+----+------+
| Id | Type |
+----+------+
|  1 |    1 |
|  4 |    1 |
|  3 |    2 |
|  7 |    3 |
|  2 |    6 |
+----+------+
5 rows in set (0.03 sec)

mysql> select * from test_b force index (primary);
+----+------+
| Id | Type |
+----+------+
|  1 |    1 |
|  2 |    6 |
|  3 |    2 |
|  4 |    1 |
|  7 |    3 |
+----+------+
5 rows in set (0.00 sec)


观察select * from test_b的前两条结果:(1,1),(4,1),当Type相等的时候,按照Id排序。为了确认这一点,再多插入点数据观察,结论相同。

mysql> insert into test_b values(9,3),(6,3),(10,3);
Query OK, 3 rows affected (0.04 sec)
Records: 3  Duplicates: 0  Warnings: 0


mysql> select * from test_b ;
+----+------+
| Id | Type |
+----+------+
|  1 |    1 |
|  4 |    1 |
|  3 |    2 |
|  6 |    3 |
|  7 |    3 |
|  9 |    3 |
| 10 |    3 |
|  2 |    6 |
+----+------+
8 rows in set (0.00 sec)


默认情况下为什么会结果按照索引列有序呢?这还要从数据库内部的运行机制说起。首先系统会查询索引表(test_b_indexed_type),该索引表的主键是索引列type(通常为了保证主键唯一性,type后面会添加一个id后缀),通过索引列查到Id,然后拿着这些Id去test_b中查询最终结果。为了最高效,扫描索引表的时候会顺着type主键往下扫,

然后拿扫得的id去“逐个”请求test_b,于是自然就出现了按照索引列有序的结果。当Type列的值一致的时候,插入到索引列的数据可以根据Id顺序插入到索引表中,

保证了当Type一致的时候,会按照Id排序。


2:在无索引的情况下。Mysql会以主键当成索引来按主键从小到大输出结果。

CREATE TABLE `test_a` (
      `Id` int(11) NOT NULL,
      `Type` int(11) DEFAULT NULL,
       PRIMARY KEY (`Id`)
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into test_a values(1,1),(2,6),(3,2),(7,3),(4,1);

mysql> select * from test_a;
+----+------+
| Id | Type |
+----+------+
|  1 |    1 |
|  2 |    6 |
|  3 |    2 |
|  4 |    1 |
|  7 |    3 |
+----+------+

看到上面的Id是按从小到大输出的了吧!


下面是别人的总结:


Select 语句如果不加 “Order By”, MySQL会怎么排序呢?

* 不能依赖 MySQL 的默认排序
* 如果你想排序,总是加上 Order By
* GROUP BY 强加了 Order By,

对于 MyISAM 表
MySQL Select 默认排序是按照物理存储顺序显示的。(不进行额外排序).
也就是说
SELECT * FROM tbl – 会产生“表扫描”。如果表没有删除、替换、更新操作,记录会显示为插入的顺序。

InnoDB 表
同样的情况,会按主键的顺序排列。但是这是不靠谱的,它是潜规则。

“Select” 不加 “Order by”时, MySQL 会尝试以尽可能快的方法(MySQL 实际的方法不见得快)返回数据。
由于访问主键、索引大多数情况会快一些(在Cache里)所以返回的数据有可能以主键、索引的顺序输出,
这里并不会真的进行排序,主要是由于主键、索引本身就是排序放到内存的,所以连续输出时可能是某种序列。
在一些情况下消耗硬盘寻道时间最短的数据会先返回。 如果只查询单个表,在特殊的情况下是有规律的。

最后总结

“Order By 是要加的”

我们对于翻页等逻辑必须默认加上order by排序,而且order by的字段如果有重复值,必须指定第二排序字段,如果第二排序字段还有重复值,那必须指定更多的字段,直到所有的排序字段能够指定唯一顺序。


### MySQL 索引的使用方法 在MySQL中,索引是一种用于加速数据检索的技术结构。通过创建索引,可以显著提高查询效率并减少磁盘I/O操作。然而,不恰当的索引设计可能会适得其反,增加存储开销写入延迟。 #### 创建索引的方法 1. **索引**索引是指基于单一字段建立的索引。例如,对于`users`表中的`login_name`字段,可以通过以下语句创建索引: ```sql CREATE INDEX idx_login_name ON users(login_name); ``` 2. **组合索引** 组合索引是在多个字段上共同构建的一个索引。相比单独为每个字段创建单索引,组合索引能够更高效地支持复杂的查询条件[^1]。例如: ```sql CREATE INDEX idx_city_age ON users(city, age); ``` 3. **全文索引** 全文索引适用于大规模文本搜索场景。它允许执行模糊匹配自然语言处理。需要注意的是,只有MyISAM引擎原生支持全文索引,而InnoDB自MySQL 5.6起也已支持。 ```sql ALTER TABLE articles ADD FULLTEXT(fulltext_index) (content); ``` 4. **唯一索引** 唯一索引确保指定的数据具有唯一性,常用于防止重复记录插入的情况。 ```sql CREATE UNIQUE INDEX unique_email_idx ON users(email); ``` --- ### 添加索引的适用场景及最佳实践 1. **高频率查询的字段** 如果某个字段经常被用作过滤条件(即出现在`WHERE`子句中),则应考虑为其添加索引。这有助于加快查询速度[^2]。 2. **避免对索引应用函数或表达式** 当在`WHERE`子句中对索引使用函数(如`YEAR()`)、算术运算或其他复杂表达式时,可能导致索引失效,从而引发全表扫描[^3]。因此,建议重构SQL语句以保持索引的有效性。 3. **合理选择索引类型** 不同类型的索引适合不同的应用场景。例如,B树索引适用于范围查找;哈希索引更适合精确匹配;全文索引则是针对大量文本内容的快速搜索需求。 4. **注意NULL值的影响** 对于含有较多`NULL`值的,需谨慎评估是否值得为其创建索引。因为大多数情况下,这些`NULL`值并不会参与比较逻辑,反而会占用额外的空间资源[^3]。 5. **监控与调整现有索引** 定期分析系统的运行状况,利用`EXPLAIN`命令查看具体查询计划,并据此判断当前索引设置是否存在冗余或不足之处。必要时可采用`DROP INDEX`删除无用索引或将旧有的单索引替换为更加高效的组合形式。 --- ### 示例代码展示 以下是几个常见的索引管理案例: #### 场景一:优化日期筛选性能 假设存在一张订单表`orders`,其中包含时间戳字段`created_at`。为了提升按年份统计销售额的速度,我们应当直接定义覆盖整个时间段的复合索引而非依赖动态计算方式。 ```sql CREATE INDEX idx_created_year_month ON orders(YEAR(created_at), MONTH(created_at)); SELECT SUM(amount) FROM orders WHERE YEAR(created_at)=2023 AND MONTH(created_at)=8; ``` #### 场景二:解决联合查询瓶颈 假设有两张关联关系紧密的大规模表格A与B之间需要频繁交互,则可通过预先规划好双方主外键对应的映射路径来降低整体耗时成本。 ```sql ALTER TABLE A ADD COLUMN b_id INT UNSIGNED NOT NULL AFTER id, ADD CONSTRAINT fk_a_b FOREIGN KEY(b_id) REFERENCES B(id); -- 同步新增辅助指引项以便后续定位更快捷 CREATE INDEX idx_fk_ab ON A(b_id); ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值