欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

关于MYSQL中每个用户取1条记录的三种写法(group by xxx)

今天以前的同学问我关于这方面的SQL语句,我特意记忆一下,毕竟这个也比较常见了
复制代码代码如下:

select * from (select * from member_payment 
                order by id desc) t group by member_id limit 10

第一种是先排序,然后group,这样的话自然可以取到最适合的一条数据。
缺点很明显:Using temporary; Using filesort
复制代码代码如下:

select s.* 
from (SELECT max(id) as id FROM `member_payment` group by `member_id` limit 10) t 
left join `member_payment` as s on t.id=s.id 

第二种是联合查询 
复制代码代码如下:

select * from `member_payment` where EXISTS (
 select `id` from (
  SELECT max(`id`) as id FROM `member_payment` group by `member_id` limit 10) t 
 where t.`id`=`member_payment`.`id`
)

第三种是子查询
窃以为第二种效率最高

[sql] view plain copy
 
  1. 完全正确的写法:    
  2. SELECT * FROM (SELECT * FROM posts ORDER BY dateline DESC)  BIAOMING GROUP BY  tid ORDER BY dateline DESC LIMIT 10    
  3.   
  4. 批评了这么多为什么不批评自己?  
来自  http://www.jb51.net/article/39369.htm


这里有2篇文章 综合起来就差不多了 工作忙 没时间整理

其实和其它数据库一样 并没有什么捷径 效率的话还是join比较快

 

 

 

 

[html] view plain copy
 
  1. MYSQL每个用户取1条记录的三种写法(group by xxx)  
  2.    
  3. 同学问我关于这方面的SQL语句,我特意记忆一下,毕竟这个也比较常见了  
  4.    
  5. [sql]   
  6. select * from (select * from member_payment     
  7.                 order by id desc) t group by member_id limit 10    
  8. 第一种是先排序,然后group,这样的话自然可以取到最适合的一条数据。  
  9.    
  10. 缺点很明显:Using temporary; Using filesort  
  11.    
  12. [sql]   
  13. select s.*     
  14. from (SELECT max(id) as id FROM `member_payment` group by `member_id` limit 10) t     
  15. left join `member_payment` as s on t.id=s.id     
  16.    
  17. 第二种是联合查询  
  18. [sql]   
  19. select * from `member_payment` where EXISTS (    
  20.     select `id` from (    
  21.         SELECT max(`id`) as id FROM `member_payment` group by `member_id` limit 10) t     
  22.     where t.`id`=`member_payment`.`id`    
  23. )    
  24. 第三种是子查询  
  25.    
  26. 窃以为第二种效率最高  


 

 

 

 

[html] view plain copy
 
  1. 类如 有一个帖子的回复表,posts( id , tid , subject , message ,  dateline ) ,  
  2.   
  3. id 为 自动增长字段, tid为该回复的主题帖子的id(外键关联),  subject 为回复标题, message 为回复内容, dateline 为回复时间,用UNIX 时间戳表示,  
  4.   
  5. 现在要求 选出 前十个来自不同主题的最新回复  
  6.   
  7.   
  8. SELECT * FROM posts GROUP BY  tid  LIMIT 10  
  9.   
  10.   
  11. 这样一个sql语句选出来的并非你想要的 最新的回复,而是最早的回复,实际上是某篇主题的第一条回复记录!  
  12.   
  13. 也就是说 GROUP BY 语句没有排序,那么怎么才能让 GROUP 按照 dateline 倒序排列呢?加上 order by 子句?  
  14.   
  15. 看下面:  
  16.   
  17. SELECT * FROM posts GROUP BY  tid  ORDER BY dateline DESC LIMIT 10  
  18.   
  19.   
  20. 这条语句选出来的结果和上面的完全一样,不过把结果倒序排列 了,而选择出来的每一条记录仍然是上面的记录,原因是 group by 会比 order by 先执行,这样也就没有办法将 group by 之前,也就是在分组之前进行排序了, 有网友会写出下面的sql 语句:  
  21.   
  22.   
  23. SELECT * FROM posts GROUP BY  tid DESC ORDER BY dateline DESC LIMIT 10  
  24.   
  25.   
  26. 也就是说 在 GROUP BY 的字段 tid 后面加上递减顺序,这样不就可以取得分组时的最后回复了吗?这个语句执行结果会和上面的一模一样,这里加上 DESC 和ASC对执行结果没有任何影响!其实这是一个错误的语句,原因是GROUP BY 之前并没有排序功能,mysql 手册上面说,GROUP BY 时是按照某种顺序排序的,某种顺序到底是什么顺序?其实根本没有顺序,因为按照tid分组,其实也就是说,把tid相等的归纳到一个组,这样想的话,GROUP BY tid DESC 可以认为是在按照 tid 分组的时候,按照tid进行倒序排列,这不扯吗,既然是按照tid分组,当然是tid相等的归到一组,而这时候按照tid倒叙还是升序有个P用!  
  27.   
  28.   
  29. 于 是有网友发明下面的语句:  
  30.   
  31.   
  32. SELECT * FROM posts GROUP BY  tid , dateline DESC ORDER BY dateline DESC LIMIT 10  
  33.   
  34.   
  35. 心 想这样我就可以在分组前按照  dateline 倒序排列了,其实这个语句并没有起到按照tid分组的作用,原因还是上面的,在group by 字段后加 desc 还是 asc 是错误的写法,而这种写法 网友本意是想 按照 tid 分组,并且在分组的时候按照 dateline排倒序!而实际这句相当于下面的写法:(去掉 GROUP BY 字段后面的 DESC)  
  36.   
  37.   
  38. SELECT * FROM posts GROUP BY  tid , dateline ORDER BY dateline DESC LIMIT 10  
  39.   
  40.   
  41. 也就是说,按照 tid 和 dateline 联合分组,只有在记录tid和dateline 同时相等的时候才归纳到一组,这显然不可能,因为 dateline 时间线基本上是唯一的!  
  42.   
  43.   
  44. 有人写出下面的语句:  
  45.   
  46.   
  47. SELECT *,max(dateline) as max_line FROM posts GROUP BY  tid ORDER BY dateline DESC LIMIT 10  
  48.   
  49.   
  50. 这条语句的没错是选出了最大发布时间,但是你可 以对比一下 dateline 和 max_dateline 并不相等!(可能有相当的情况,就是分组的目标记录只有一条的时候!)  
  51.   
  52.   
  53. 为 什么呢?原因很简单,这条语句相当于是 在group by 以后选出本组的最大的发布时间!对分组没有起到任何影响!因为SELECT子句是最后执行的!  
  54.   
  55. 后来更有网友发明了下面的写法!  
  56.   
  57.   
  58. SELECT *,max(dateline) as max_line FROM posts GROUP BY  tid HAVING dateline=max(dateline)  
  59.   
  60.   
  61. ORDER BY dateline DESC LIMIT 10  
  62.   
  63.   
  64. 这条语句的预期结果和想象中的并不相同!因为你会发现,分组的结果 中大量的记录没有了!为什么?因为 HAVING 是在分组的时候执行的,也就说:在分组的时候加上一个这样的条件:选择出来的 dateline 要和 本组最大的dateline 相等,执行的结果和下面的语句相同:  
  65.   
  66.   
  67. SELECT *,max(dateline) as max_line FROM posts GROUP BY  tid HAVING count(*)=1  
  68.   
  69.   
  70. ORDER BY dateline DESC LIMIT 10  
  71.   
  72.   
  73. 看 了这条sql语句是不是明白了呢?  
  74.   
  75. dateline=max(dateline) 只有在分组中的记录只有一条的时候才成立,原因很明白吧!只有一条他才会和本组的最大发布时间相等阿,(默认dateline为不重复的值)  
  76.   
  77.   
  78. 原 因还是因为 group by 并没有排序功能,所有的这些排序功能只是错觉,所以你最终选出的 dateline 和max(dateline) 永远不可能相等,除非本组的记录只有一条!GROUP BY 在分组的时候,可能是一个一个来找的,发现有相等的tid,去掉,保留第一个发现的那一条记录,所以找出来的记录永远只是按照默认索引顺序排列的!  
  79.   
  80.   
  81. 那 么说了这么多,到底有没有办法让 group by 执行前分组阿?有的 ,子查询阿!  
  82.   
  83.   
  84. 最简单的 :  
  85.   
  86.   
  87. SELECT * FROM (SELECT * FROM posts ORDER BY dateline DESC) GROUP BY  tid ORDER BY dateline DESC LIMIT 10  
  88.   
  89.   
  90. 也 有网友利用自连接实现的 ,这样的效率应该比上面的子查询效率高,不过,为了简单明了,就只用这样一种了,GROUP BY没有排序功能,可能是mysql弱智的地方,也许是我还没有发现,  
  91.   
  92. 期待高人拍砖!  
  93.    
  94.   
  95. 完全正确的写法:  
  96. SELECT * FROM (SELECT * FROM posts ORDER BY dateline DESC)  BIAOMING GROUP BY  tid ORDER BY dateline DESC LIMIT 10  


 

版权声明:本文为博主原创文章,未经博主允许不得转载。
 
Jim_Parsons
  • Jim_Parsons

    2017-08-12 10:033楼
  • [sql] view plain copy
     
    1. 完全正确的写法:    
    2. SELECT * FROM (SELECT * FROM posts ORDER BY dateline DESC)  BIAOMING GROUP BY  tid ORDER BY dateline DESC LIMIT 10    
    3.   
    4. 批评了这么多为什么不批评自己?  
 
Jack_ZZ_Zheng
  • Jack_ZZ_Zheng

    2017-08-07 15:352楼
  • 不同地方看到这篇文章三次,有意思么?你自己试过这内容是否正确吗?
 
zjshishen1210
  • zjshishen1210

    2017-08-07 11:391楼
  • 一直用的JOIN,没用过EXISTS,用explain看了一下,和JOIN是差不多的;也有可能是我表结构的问题。
  •  
 
 
普通分类: