数据表结构和数据如下:

CREATE TABLE `commun_message_chat_single` (
`id` int() NOT NULL AUTO_INCREMENT,
`chat_id` int() DEFAULT '' COMMENT '会话id',
`from_id` varchar() DEFAULT NULL COMMENT '发送者 用户id',
`to_id` varchar() DEFAULT NULL COMMENT '接收者 用户id',
`content` text COMMENT '消息内容',
`type` tinyint() DEFAULT '' COMMENT '消息类型 1:文字 2:图片 3:文件',
`send_time` datetime DEFAULT NULL COMMENT '消息发送时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT= DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='单聊 聊天记录'; -- 插入数据的sql
INSERT INTO `commun_message_chat_single` VALUES (, , '', 'md_1', '', , '2019-10-16 08:25:50');
INSERT INTO `commun_message_chat_single` VALUES (, , '', 'md_1', '', , '2019-10-28 08:25:59');
INSERT INTO `commun_message_chat_single` VALUES (, , '', 'md_2', '', , '2019-10-01 08:26:21');
INSERT INTO `commun_message_chat_single` VALUES (, , '', 'md_2', '哈哈哈', , '2019-10-27 08:26:34');
INSERT INTO `commun_message_chat_single` VALUES (, , '', 'md_2', '测试测试', , '2019-10-10 08:28:27');

目前数据表所有数据如下:

mysql> select * from commun_message_chat_single where from_id = '11';
+----+---------+---------+-------+--------------+------+---------------------+
| id | chat_id | from_id | to_id | content | type | send_time |
+----+---------+---------+-------+--------------+------+---------------------+
| 60 | 10 | 11 | md_1 | 123 | 1 | 2019-10-16 08:25:50 |
| 61 | 10 | 11 | md_1 | 456 | 1 | 2019-10-28 08:25:59 |
| 62 | 10 | 11 | md_2 | 789 | 1 | 2019-10-01 08:26:21 |
| 63 | 10 | 11 | md_2 | 哈哈哈 | 1 | 2019-10-27 08:26:34 |
| 64 | 10 | 11 | md_2 | 测试测试 | 1 | 2019-10-10 08:28:27 |
+----+---------+---------+-------+--------------+------+---------------------+
5 rows in set (0.00 sec)

需求:查询from_id为11的数据 并且 和 每一个to_id 按照时间排序显示最新的一条数据(也就是显示:to_id是md_1的,按照时间排序id为61的符合结果;to_id是md_2的,按照时间排序id为63的符合结果)

符合该需求的2条数据如下:

+----+---------+---------+-------+-----------+------+---------------------+
| id | chat_id | from_id | to_id | content | type | send_time |
+----+---------+---------+-------+-----------+------+---------------------+
| 61 | 10 | 11 | md_1 | 456 | 1 | 2019-10-28 08:25:59 |
| 63 | 10 | 11 | md_2 | 哈哈哈 | 1 | 2019-10-27 08:26:34 |
+----+---------+---------+-------+-----------+------+---------------------+

实现该需求的sql语句如下(利用sql中的子查询):

SELECT * FROM (SELECT * FROM commun_message_chat_single WHERE from_id = '11' ORDER BY send_time DESC ) as
temp_table GROUP BY temp_table.to_id; -- 大概解释下该条sql语句:括号内的子查询是查询from_id为11的数据并且按照send_time从高到低排序,这里的子查询的结果会生成一个临时表,临时表这里取名为temp_table,然后外部查询将temp_table的结果进行分组。

mysql5.7版本以下执行结果如下(只在5.5和5.6版本试过):

+----+---------+---------+-------+-----------+------+---------------------+
| id | chat_id | from_id | to_id | content | type | send_time |
+----+---------+---------+-------+-----------+------+---------------------+
| 61 | 10 | 11 | md_1 | 456 | 1 | 2019-10-28 08:25:59 |
| 63 | 10 | 11 | md_2 | 哈哈哈 | 1 | 2019-10-27 08:26:34 |
+----+---------+---------+-------+-----------+------+---------------------+
2 rows in set (0.00 sec)

 mysql5.7版本执行结果如下:

+----+---------+---------+-------+---------+------+---------------------+
| id | chat_id | from_id | to_id | content | type | send_time |
+----+---------+---------+-------+---------+------+---------------------+
| | | | md_1 | | | -- :: |
| | | | md_2 | | | -- :: |
+----+---------+---------+-------+---------+------+---------------------+
rows in set (0.00 sec)

what?为啥5.7以下的版本是我们想要的结果,而5.7版本的执行结果居然不是我们期待的结果!高版本居然执行的结果不正确。。

为什么mysql5.7和5.7以下的版本会有不同的结果呢?

可以分别查看一下这条sql语句在两个不同版本数据库的sql执行计划:

mysql 5.7.21:

mysql5.5.62和5.6.44:

对比可以发现5.7版本的mysql在执行这条sql语句的时候缺少了一个derived的操作,通过查阅相关资料了解到mysql5.7对子查询进行了优化,认为子查询中的order by可以进行忽略,只要Derived table里不包含如下条件就可以进行优化:

①、UNION clause

②、GROUP BY

③、DISTINCT

④、Aggregation

⑤、LIMIT or OFFSET

看到了吧,如果要在mysql5.7中实现先排序后分组,这里可以加个limit,不过你的limit要足够大

mysql5.7解决办法如下:

SELECT * FROM (SELECT * FROM commun_message_chat_single WHERE from_id = '' ORDER BY send_time DESC LIMIT
) as temp_table GROUP BY temp_table.to_id;

mysql8.0及以上版本没试过,不过应该和5.7是一样的效果,都进行了优化。

PS:子查询不是mysql独有的,sqlserver等数据库也可以使用子查询

sql中实现先排序后分组的更多相关文章

  1. MySql下实现先排序后分组

    最近在工作中遇到一个先排序后分组的需求,发现MySql不同的版本有不同的结果,特此记录. 举例:要求在shop表中查询出各类型商店中价格最高的商品. --表结构-- create table `sho ...

  2. Java基础知识强化之IO流笔记52:IO流练习之 把一个文件中的字符串排序后再写入另一个文件案例

    1. 把一个文件中的字符串排序后再写入另一个文件 已知s.txt文件中有这样的一个字符串:"hcexfgijkamdnoqrzstuvwybpl" 请编写程序读取数据内容,把数据排 ...

  3. SQL中条件放在on后与where后的区别

    SQL中on条件与where条件的区别 数据库在通过连接两张或多张表来返回记录时,都会生成一张中间的临时表,然后再将这张临时表返回给用户. 在使用left jion时,on和where条件的区别如下: ...

  4. SQL中前置0和后置0的处理问题

    在sql语句中经常遇到处理前置和后置数据的问题 1.首先使用convert转化函数对预处理的数据进行转化,CONVERT()函数可以将制定的数据类型转换为另一种数据类型 MySQL 的CAST()和C ...

  5. sql中的group by 和 having 用法解析

    转载博客:http://www.cnblogs.com/wang-123/archive/2012/01/05/2312676.html --sql中的group by 用法解析:-- Group B ...

  6. sql中group by 和having 用法解析

    --sql中的group by 用法解析:-- Group By语句从英文的字面意义上理解就是“根据(by)一定的规则进行分组(Group)”.--它的作用是通过一定的规则将一个数据集划分成若干个小的 ...

  7. mysql 怎样先排序再分组

    权游游牧族:众所周知!一句SqL语句不能先排序再分组.所以这里给出几个案例 --表结构-- create table `shop` ( `id` int (10) PRIMARY KEY, `shop ...

  8. 在oracle中,group by后将字符拼接,以及自定义排序

    1.在oracle中,group by后将字符拼接.任务:在学生表中,有studentid和subject两个字段.要求对studentid进行group by分组,并将所选科目拼接在一起.oracl ...

  9. mssql sqlserver 使用sql脚本获取群组后,按时间排序(asc)第一条数据的方法分享

    摘要: 下文讲述使用sql脚本,获取群组后记录的第一条数据业务场景说明: 学校教务处要求统计: 每次作业,最早提交的学生名单下文通过举例的方式,记录此次脚本编写方法,方便以后备查,如下所示: 实现思路 ...

随机推荐

  1. ES6学习总结之Set和Map数据结构的理解

    前言 当我们需要存储一些数据的时候,首先想到的是定义一个变量用来存储,之后我们可能学了数组,发现数组比变量可以存储更多的数据,接着可能有其它的存储数据的方法等等,然而我今天需要介绍的是在ES6中比较常 ...

  2. Win10下80端口被System占用导致Apache无法启动

    Windows10下80端口被PID为4的System占用导致Apache无法启动的分析与解决方案 方法/步骤     最近更新了Windows10,总体上来说效果还是蛮不错的,然而今天在开启Apac ...

  3. [Note] Windows 10 Python 3.6.4 安装scrapy

    直接使用pip install安装时会在安装Twisted出错,以下主要是解决Twisted的安装问题 1. 安装wheel pip install wheel 2. 安装Twisted 在Pytho ...

  4. git checkout 提示 “error: The following untracked working tree files would be overwritten by checkout” 解决

    问题描述 Windows 或者 macOS 操作系统中,文件名是不区分大小写的.对于已经提交到仓库中的文件修改文件名的大小写,然后又用 git rm 命令将老文件从 Git 仓库删除掉,并保存下新的文 ...

  5. 使用Xhell连接Docker虚拟机

    平时我们可以使用Xhell来连接Docker虚拟机. 1.首先,我们来用VirtualBox管理器来看一下default虚拟机的网络配置: 2.新建Xshell会话: a.我们可以看到ssh的主机IP ...

  6. SQL SERVER 数据库中查看文本字段中的数据长度LEN() 函数的使用方法

    SQL LEN() 语法 SELECT LEN(column_name) FROM table_name Id LastName FirstName Address City 1 Adams John ...

  7. 子网掩码!如何划分子网掩码,计算IP地址

    作者:chli1806 一.子网掩码的含义和根据子网掩码划分子网一个IP地址必然属于某一个网络,或者叫子网.子网掩码就是用来指定某个IP地址的网络地址的,换一句话说,就是用来划分子网的.例如,一个A类 ...

  8. Vue中组件

    0828自我总结 Vue中组件 一.组件的构成 组件:由 template + css + js 三部分组成(.vue文件) 1)组件具有复用性 2) 复用组件时,数据要隔离 3) 复用组件时,方法不 ...

  9. POWERSPLOIT-Recon(信息侦察)脚本渗透实战

    Recon(信息侦察)模块 a) 调用invoke-Portscan扫描内网主机的端口. 1)通过IEX下载并调用invoke-portscan. PS C:\Users\Administrator& ...

  10. JAVA锁有哪些种类,以及区别

    原文链接:https://www.cnblogs.com/lxmyhappy/p/7380073.html 在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内 ...