Mysql版本差异导致groupBy+orderBy结果不一致的问题

it2024-08-01  37

发现个问题。 Mysql版本导致orderBy + groupBy之后的结果不一致 在Mysql5.6版本和5.7版本稳定复现。

举个栗子🌰: tests表中数据如下 想查找每个relate_id下created_at最大的数据 sql如下

select id,relate_id from (select id,relate_id from tests ORDER BY created_at desc) as t GROUP BY relate_id;

在5.6版本运行结果如下:

可以看出是能按照我们的需求找到ID最大的,也就是created_at最大的数据的

但是在5.7及以上版本的时候,结果却完全相反:

取的是最小的那个,看上去似乎是我的orderby desc并没有生效。 执行了一下explain

在网上查询了一下关于这个的原因,也就是Mysql 5.7中的Derived table。 Derived table是个啥玩意呢?其实你可以理解为一个子查询,位于sql语句的from子句里面,在5.7之前的版本都是把Derived table 进行Materialize,生成一个临时表保存Derived table的结果,然后进行join之类的操作完成整个查询。 但是5.7版本之后呢,对Derived table做了一个新特性,这个新特新就允许符合条件的子表直接和父查询的表进行join等操作。

Merge Derived table有两种方式进行控制。第一种,通过开关optimizer_switch=’derived_merge=on|off’来进行控制。 第二种,在CREATE VIEW的时候指定ALGORITHM=MERGE | TEMPTABLE, 默认是MERGE方式。如果指定是TEMPTABLE,将不会对VIEW进行Merge Derived table操作。

但是仍然有很多限制,只要Derived table里不包含如下条件就可以利用该特性进行优化:

UNION clauseGROUP BYDISTINCTAggregationLIMIT or OFFSETDerived table里面包含用户变量的设置。 也就是如上查询,在5.7版本中只要添加一个最简单的limit就能使 5.7中Derived table新特性失效,从而达到我们想要的效果~ EXPLAIN select id,relate_id from (select id,relate_id from tests ORDER BY created_at desc limit 1000) as t GROUP BY relate_id;

看下现在的explain: 当然根据上面几种给出的情况只要不影响你的sql结果,你添加上相关的查询就能达到一样的效果。

那么追根到底就是说Derived table作为一个子表join到父查询的表的时候,忽略了orderBy语句,导致了查询的差异。以下是我找到的关于这两个部分处理的原则,仅供参考~

如果Derived table中包含ORDER By语句,处理原则和正常SubQuery的处理方式类似: 1. 如果Derived table只包含一个表 2. 并且Derived table不包含聚集函数 满足上述两个条件之后,Derived table将会保留ORDER BY。其他情况subquery中的ORDER BY将会被忽略掉,这也是MySQL5.7区别于MySQL5.6的一点。 当Derived table保留了Order by,是否能合并到父查询,需要满足如下条件: 1. 父查询允许做Derived table中的ORDER BY。下面几种情况不允许做ORDER BY a) 如果父查询包含有自己的ORDER BY b) 如果父查询包含GROUP BY c) 如果父查询包含未被优化掉的DISTINCT 2. 父查询不能是UNION操作,因为UNION默认会做DISTINCT操作 3. 为了简化操作,只有当父查询只包含Derived table的时候(即FROM子句里面只有Derived table一个表)才可以保留ORDER BY。这里有相当大的改进空间可以尽量的来按照Derived table定义的ORDER BY操作来进行父查询的操作。比如有两个表以上,如果父查询没有ORDER BY的要求,也可以按照Derived table来对结果进行排序。
最新回复(0)