mysql分组后,取每组第一条数据

在mysql 5.7之前(不包括5.7)

select * from (select a.* from template_detail a
               where a.template_id in (3, 4)
              order by a.id desc) tt
group by tt.template_id;

但是在mysql 5.7之前(包括5.7),这样查询会发现order by 失效

是因为mysql 5.7引入了derived_merge 

什么是derived_merge?
derived_merge指的是一种查询优化技术,作用就是把派生表合并到外部的查询中,提高数据检索的效率。这个特性在MySQL5.7版本中被引入,可以通过如下SQL语句进行查看/开启/关闭等操作。

上面虽然听起来感觉很牛逼的样子,但是实际情况是,这个新特性,不怎么受欢迎,容易引起错误。

可以在子查询中使用以下函数来进行关闭这个特性:

可以通过在子查询中使用任何阻止合并的构造来禁用合并,尽管这些构造对实现的影响并不明确。 防止合并的构造对于派生表和视图引用是相同的:

  1.    聚合函数( SUM() , MIN() , MAX() , COUNT()等)
  2.    DISTINCT
  3.    GROUP BY
  4.    HAVING
  5.    LIMIT
  6.    UNION或UNION ALL
  7.    选择列表中的子查询
  8.    分配给用户变量
  9.    仅引用文字值(在这种情况下,没有基础表)

所以mysql5.7比较常见的实现方法是:

select * from (select a.* from template_detail a
               where a.template_id in (3, 4)
              order by a.id desc limit 10000) tt
group by tt.template_id;

这一种也是网上推荐最多的,但个人觉得局限性太大,不介意用在实战上

推荐写法:

select * from (select distinct(a.id) tid, a.* from template_detail a
               where a.template_id in (3, 4)
              order by a.id desc) tt
group by tt.template_id;

加了distinct(a.id) tid, 后结果正确,原因是因为临时表(派生表derived table)中使用order by且使其生效,必须满足三个条件:

  1. 外部查询禁止分组或者聚合
  2. 外部查询未指定having,HAVING, order by
  3. 外部查询将派生表或者视图作为from句中唯一指定源

不满足这三个条件,order by会被忽略。

一旦外部表使用了group by,那么临时表(派生表 derived table)将不会执行filesort操作(即order by 会被忽略),所以我在临时表中加了(distinct(a.id))。
加了之后就相当于关闭了该特性,所以也就生效了。

 或者这样效率更高一点:

SELECT bb.`detail`, bb.`id`,bb.`template_id` 
from `template_detail` bb 
		INNER JOIN 
		( 
		    SELECT MAX(`id`) id, `template_id` 
		    from `template_detail` GROUP BY `template_id` 
		) as tb on bb.`id` = tb.id

猜你喜欢

转载自blog.csdn.net/sunyanchun/article/details/128447126