业务场景
有两张表:表A,和表B,需要对A中数据按排序字段排序,对B表也按排序字段排序,然后返回并集。
写出如下SQL(已简化):
(select id from A order by sort desc)
union all
(select id from B order by sort desc);
初看似乎没什么问题,数据被筛选出来了,且数量正确,但是,排序却没有按照如期的顺序排列。
验证
再次简化上述SQL,只取一张表的数据:
(select id from A order by sort)
union all
(select id from A order by sort desc);
预期结果:第一条ID=最后一条ID。
实际结果:两条子查询的sql都按正序排列,排序似乎未生效。
无论是按正序还是倒叙排,结果总是正序,因此可能是mysql将子查询的order by
优化掉了。
查看下优化后的sql(已简化):
(select id from A)
union all
(select id from A);
order by
已经被移除。所以无论如何排序都是不生效的。
💡 如何查看优化后的SQL?
在IDEA或者DataGrip中,执行:explain <sql语句>,将会返回优化后的实际执行的sql,例如:
原因
在MySQL文档中,找到了对union
的描述,其中有如下一段:
Use of ORDER BY for individual SELECT statements implies nothing about the order in which the rows appear in the final result because UNION by default produces an unordered set of rows. Therefore, ORDER BY in this context typically is used in conjunction with LIMIT, to determine the subset of the selected rows to retrieve for the SELECT, even though it does not necessarily affect the order of those rows in the final UNION result. If ORDER BY appears without LIMIT in a SELECT, it is optimized away because it has no effect.
Ref:https://dev.mysql.com/doc/refman/5.7/en/union.html
对单个SELECT语句使用ORDER BY并不意味着行在最终结果中出现的顺序,因为UNION默认情况下会产生一组无序的行。因此,在这种情况下,ORDER BY通常与LIMIT结合使用,以确定要为SELECT检索的选定行的子集,即使它不一定影响最终UNION结果中这些行的顺序。如果ORDER BY在SELECT中没有出现LIMIT,则它会被优化掉,因为它没有效果。
🤔 思考:为什么加limit
后就必须排序而不能优化掉呢?
😊 原因:limit
的本意是取指定数量的结果,当有order by
时,需要在排序之后再取指定数量的结果。所以,当在子查询中加上limit后,不得不进行排序,如果不排序,那这个子查询本身的语义就是错误的,即limit
让order by
强制执行了。
所以,对于上述场景的SQL,在子查询中加上合适的limit
即可解决。当让,如果不需要局部有序,而是结果集整体有序,则再套一层后在外层排序即可。