Mysql 查询主键未指定排序时的默认排序问题

本文探讨了在MySQL中使用主键查询时遇到的排序问题。通过对比不同查询语句和执行计划,揭示了为何直接查询主键可能不会按照预期排序,以及如何通过强制索引或添加ORDER BY子句来解决此问题。

正文

跑批量任务需要分批按顺序把主键取出来,语句如下:

SELECT id FROM foo.bar LIMIT 10 OFFSET 0
+-----+
| id  |
+-----+
| 109 |
| 13  |
| 14  |
| 15  |
| 128 |
| 129 |
| 130 |
| 190 |
| 226 |
| 227 |
+-----+
复制代码

发现虽然用主键去查,但结果没有按照主键排序。

查询*试试

SELECT * FROM foo.bar LIMIT 10 OFFSET 0
+----+-------+---+
| id | a     | b |
+----+-------+---+
| 1  | 24274 | 0 |
| 2  | 24274 | 0 |
| 3  | 24274 | 0 |
| 4  | 24274 | 0 |
| 5  | 24274 | 0 |
| 6  | 24274 | 0 |
| 7  | 24274 | 0 |
| 8  | 24274 | 0 |
| 9  | 24274 | 0 |
| 10 | 24274 | 0 |
+----+-------+---+
复制代码

排序按照主键。

查看执行计划,结果如下:

EXPLAIN SELECT * FROM foo.bar LIMIT 10 OFFSET 0 \G
***************************[ 1. row ]***************************
id            | 1
select_type   | SIMPLE
table         | bar
partitions    | <null>
type          | ALL
possible_keys | <null>
key           | <null>
key_len       | <null>
ref           | <null>
rows          | 211
filtered      | 100.0
Extra         | <null>
复制代码

发现select *没走索引,使用了全表扫描,因此顺序为主键顺序。

EXPLAIN SELECT id FROM foo.bar LIMIT 10 OFFSET 0 \G
***************************[ 1. row ]***************************
id            | 1
select_type   | SIMPLE
table         | bar
partitions    | <null>
type          | index
possible_keys | <null>
key           | idx_a
key_len       | 8
ref           | <null>
rows          | 211
filtered      | 100.0
Extra         | Using index
复制代码

而select id并没有用到聚簇索引。innodb二级索引会自动添加主键作为索引列最后一项,使用该索引也能做到覆盖查询。查询优化器使用该索引,导致返回的顺序不符合预期。

SELECT a,id FROM foo.bar LIMIT 10 OFFSET 0
+------+-----+
| a    | id  |
+------+-----+
| 1004 | 109 |
| 1823 | 13  |
| 1823 | 14  |
| 1823 | 15  |
| 1823 | 128 |
| 1823 | 129 |
| 1823 | 130 |
| 1823 | 190 |
| 1823 | 226 |
| 1823 | 227 |
+------+-----+
复制代码

发现果然之前select id用的是a的索引,并且是按照a,id的顺序排序。

SELECT id FROM foo.bar FORCE INDEX(PRI) LIMIT 10 OFFSET 0
+----+
| id |
+----+
| 1  |
| 2  |
| 3  |
| 4  |
| 5  |
| 6  |
| 7  |
| 8  |
| 9  |
| 10 |
+----+
复制代码

强制使用主键索引,果然没问题了。

explain SELECT id FROM boss_business.boss_block_refund_order order by id LIMIT 10 OFFSET 0 \G
***************************[ 1. row ]***************************
id            | 1
select_type   | SIMPLE
table         | boss_block_refund_order
partitions    | <null>
type          | index
possible_keys | <null>
key           | PRIMARY
key_len       | 8
ref           | <null>
rows          | 10
filtered      | 100.0
Extra         | Using index
复制代码

或者使用order by id引导查询优化器使用主键索引也可以。

另外需要注意,MyISAM引擎表在没有任何的删除、修改操作下,执行select 不带order by,那么会按照插入顺序进行排序。因为使用MyISAM存储引擎的表会把索引信息另外存储到一个称为索引文件的另一个文件中。总之Mysql的查询优化器一定会倾向于使用最优的方式。

参考链接

<think>好的,我现在需要回答用户关于MySQL查询未指定ORDER BY的结果集顺序规则的问题。首先,我得回顾一下自己掌握的知识,并结合提供的引用来确保回答准确。 用户的问题很明确,想知道当没有使用ORDER BYMySQL返回的结果顺序是怎样的。根据我之前的学习,我记得在这种情况下,MySQL并不会保证结果的顺序是确定的。这可能和底层的数据存储、索引使用有关。不过需要具体分析,可能还需要查看官方文档或者可靠的资料来确认。 现在看一下用户提供的引用内容。引用[2]提到,当不加ORDER BY,结果的顺序可能与查询列有关,因为不同的列可能使用不同的索引,而不同的索引会导致不同的数据顺序。这进一步说明,未指定排序顺序是不确定的,依赖于MySQL的内部机制,比如使用的索引类型。例如,如果查询使用了某个索引,那么结果可能会按照该索引的顺序返回,但如果没有使用索引,可能会按照物理存储顺序返回。 引用[4]提到,可以通过ORDER BY FIELD来指定自定义排序,这间接说明默认情况下顺序是不确定的,需要显式指定才能确保顺序。此外,引用[3]中的例子显示,当使用ORDER BY,结果按照指定列排序,而如果没有的话,顺序可能是不确定的。 还需要注意,即使多次执行相同的查询,没有ORDER BY的情况下,结果顺序是否可能变化?比如当数据发生更新、索引重建等情况,存储顺序可能改变,进而影响查询结果的顺序。因此,为了稳定性,必须显式使用ORDER BY。 另外,考虑到不同的存储引擎,比如InnoDB和MyISAM,在无ORDER BY默认行为是否有差异?例如,InnoDB作为默认引擎,其聚簇索引的数据存储方式可能导致主键顺序影响结果顺序,而MyISAM可能按插入顺序返回。但如果没有主键或索引的情况下,顺序可能更不可预测。 总结一下,我需要向用户传达几个关键点:未指定ORDER BYMySQL不保证结果顺序;顺序可能受索引、存储引擎、查询条件等因素影响;为确保一致,必须显式使用ORDER BY。同,需要引用提供的资料来支持这些观点,比如引用[2]中的内容说明不同索引导致不同顺序,引用[4]强调自定义排序的必要性。 可能还需要提到,在开发中依赖默认顺序是不安全的,尤其是在数据更新后,同样的查询可能返回不同的顺序,导致应用程序出现不可预知的行为。因此,最佳实践是始终指定ORDER BY子句,除非确实不关心顺序。 现在组织回答结构,首先明确结论,然后分点解释影响因素,最后给出建议,并引用相关资料作为支持。确保语言清晰,符合用户要求的中文回答,同正确使用LaTeX格式的数学表达式(如果有的话,不过这个问题可能不需要数学公式,但需要检查用户的问题是否涉及)。 最后,生成相关问题,比如关于ORDER BY的使用方法、性能影响、如何自定义排序等,帮助用户进一步了解相关内容。</think>MySQL查询未指定$ORDER\ BY$的结果集顺序规则可总结为以下要点: ### 一、默认排序的非确定性 1. **无明确保证** MySQL在未显式使用$ORDER\ BY$,**不保证结果集的顺序稳定**。即使多次执行相同查询,顺序可能因底层存储变化而不同[^2]。 2. **依赖存储引擎特性** - **InnoDB引擎**:若表定义了主键,可能按主键顺序返回;无主键,按行插入顺序或内部生成的ROWID顺序返回。 - **MyISAM引擎**:通常按数据物理存储顺序返回,但表碎片整理后顺序可能改变。 ### 二、影响因素 1. **索引使用** 查询是否使用索引会直接影响结果顺序。例如: ```sql -- 使用索引idx_a,结果可能按字段a的顺序返回 SELECT a FROM table WHERE a > 10; -- 若强制不使用索引,顺序可能变为物理存储顺序 SELECT a FROM table USE INDEX() WHERE a > 10; ``` 不同索引选择会导致不同排序逻辑[^2]。 2. **查询优化器决策** MySQL可能根据统计信息调整执行计划,例如选择全表扫描或索引扫描,导致顺序差异。 ### 三、典型场景示例 | 场景 | 可能顺序 | 风险 | |------|----------|------| | 新增数据后查询 | 新数据可能出现在末尾或中间 | 分页数据重复/遗漏 | | 重建索引后查询 | 按新索引结构顺序返回 | 历史数据顺序变化 | ### 四、最佳实践 1. **显式指定$ORDER\ BY$** 即使需要"默认顺序",也应明确排序条件: ```sql -- 明确按主键排序 SELECT * FROM table ORDER BY id; ``` 2. **自定义排序规则** 使用$ORDER\ BY\ FIELD()$实现特定顺序: ```sql -- 按id值为3,2,1的顺序返回 SELECT * FROM table WHERE id IN (1,2,3) ORDER BY FIELD(id,3,2,1); ``` 这种方法尤其适用于枚举值或固定列表排序[^4]。 ### 五、性能权衡 | 方法 | 优点 | 缺点 | |------|------|------| | 不加$ORDER\ BY$ | 执行速度快 | 顺序不可控 | | 加$ORDER\ BY$ | 顺序稳定 | 可能增加排序开销 | 如需优化排序性能,可考虑为排序字段添加索引,例如: ```sql ALTER TABLE orders ADD INDEX idx_order_date (order_date); ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值