mysql中group by分组查询中查询最新字段值的sql语句怎么写?

背景

今天看代码的时候,看到一个比较复杂的sql语句,我知道这条sql语句最终想要实现的结果,所以我就想自己写sql来实现一下,看看速度会不会更好,然后发现别人的sql写法执行效率更高,由此打开了一个新的世界。

需求

在聊天消息记录表中查询该用户和其他消息发送者的“最近一条未读消息id、最近一条未读消息内容、最近一条未读消息接收时间、未读消息数量、消息发送者id、消息发送者名称”。其中表名是msg_record,相关字段如下:

列名解释
id消息记录id
toId消息接收者id
fromId消息发送者id
fromUsername消息发送者名称
date消息发送日期
hasRead是否已读
msgText消息内容

效率不高的sql写法

需求分析:

需要查询的结果中包含“最近一条未读消息id、最近一条未读消息内容、最近一条未读消息接收时间”,针对“最近一条未读消息接收时间”来说,可以使用聚合函数max(),但是剩余两个都是文本性质的内容,那就只能通过排序获取,针对Mysql数据库来说,如果我们获取非分组字段,那Mysql数据库会把分组中的第一条返回,所以利用这个特性,我们就先执行排序,然后在分组,之后利用Mysql的上述特性完成数据的获取工作

sql语句:

SELECT
	a.id,
	a.fromId,
	a.fromUsername,
	a.date,
	a.msgText lastMsg,
	count( a.id ) unReadCount
FROM
	(
-- 	子查询
	SELECT
		id,
		fromId,
		fromUserName,
		date,
		msgText 
	FROM
		msg_record
	WHERE
		fromId != "4ebd6f3485f140888ecc25c12e5105b1" 
		AND toId = "4ebd6f3485f140888ecc25c12e5105b1" 
		AND hasRead = FALSE 
	ORDER BY
		date DESC 
	) a 
GROUP BY
	a.fromId

执行分析:

在上述sql前面添加EXPLAIN字段即可,我们看下分析结果:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1PRIMARY<derived2>ALL7Using temporary; Using filesort
2DERIVEDimq_server_messagerecordALLIDX_IMQ_SERVER_MESSAGERECORD_TOID,IDX_IMQ_SERVER_MESSAGERECORD_FROMIDIDX_IMQ_SERVER_MESSAGERECORD_TOID403117Using where; Using filesort

上述第二行代表子查询使用的是全表扫描,但是使用到了索引;第一行代表全表扫描,但是没有使用索引,两个都是全表扫描,说明效率是真的不高

经测试发现,执行时间大致是0.024秒左右

效率高的sql写法

需求分析:

既然先排序的方式不行,那我们就采用先分组在排序的方式来得到最终结果,这就需要使用到SUBSTRING_INDEX、GROUP_CONCAT、CONCAT函数,我们来解释一下这几个函数的含义:

  • SUBSTRING_INDEX:字符串切分函数
  • GROUP_CONCAT:字符串分组连接函数
  • CONCAT:字符串连接函数

详细分析可看:mysql取出每个分组中最新的记录(坑点勿踩)

sql语句:

SELECT
	SUBSTRING_INDEX( GROUP_CONCAT( CONCAT( id, '*splits*,' ) ORDER BY date DESC ), '*splits*,', 1 ) id,
	fromId,
	fromUsername,
	max( date ) date,
	SUBSTRING_INDEX( GROUP_CONCAT( CONCAT( msgText, '*splits*,' ) ORDER BY date DESC ), '*splits*,', 1 ) lastMsg,
	count( id ) unReadCount 
FROM
	msg_record
WHERE
	toId = "4ebd6f3485f140888ecc25c12e5105b1" 
	AND fromId != "4ebd6f3485f140888ecc25c12e5105b1" 
	AND hasRead = FALSE 
GROUP BY
	fromId

执行分析:

在上述sql前面添加EXPLAIN字段即可,我们看下分析结果:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEIMQ_SERVER_MESSAGERECORDrefIDX_IMQ_SERVER_MESSAGERECORD_TOID,IDX_IMQ_SERVER_MESSAGERECORD_FROMIDIDX_IMQ_SERVER_MESSAGERECORD_TOID403const51Using where; Using filesort

可以看到使用的是ref扫描,并且使用到了索引,并且是常量类型的索引,这肯定速度就比较快。

经测试发现,执行时间大致是0.015秒左右

我们对上面那几个函数的使用情况进行一个解释,我们先使用CONCAT()函数对属性id进行字符串拼接,然后使用GROUP_CONCAT()按照时间进行排序之后在进行字符串拼接,这样就是一个很大的字符串了,之后在使用SUBSTRING_INDEX()进行按照分隔符进行数据截取,就可以获取到结果了

但是这种处理方式也有一个弊端,如果数据量很大的话,通过这几个函数执行相关操作,那就可能会出现内存泄露的问题,但是大部分情况还好

拓展

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 您可以使用MySQLGROUP BY语句和MAX()聚合函数来查询某个时间最大,然后按照某个字段分组。假设您要查询的表名为`table_name`,时间字段为`time_field`,分组字段为`group_field`,则可以使用以下查询语句: ``` SELECT group_field, MAX(time_field) AS max_time FROM table_name GROUP BY group_field; ``` 这将返回每个分组字段的最大时间。如果您还需要包括其他字段,则可以将它们添加到SELECT语句,并将它们添加到GROUP BY语句,如下所示: ``` SELECT group_field, other_field, MAX(time_field) AS max_time FROM table_name GROUP BY group_field, other_field; ``` 这将返回每个分组字段和其他字段的最大时间。 ### 回答2: 要查询某个时间字段的最大,并按照另一个字段进行分组,可以使用以下SQL语句: ```sql SELECT 分组字段, MAX(时间字段) AS 最大时间 FROM 表名 GROUP BY 分组字段; ``` 具体步骤如下: 1. 使用SELECT语句来选择字段,并使用MAX函数获取时间字段的最大。将结果命名为"最大时间"。 2. 使用FROM语句指定要查询的表名。 3. 使用GROUP BY语句来按照指定的字段进行分组。 4. 最后,执行上述SQL语句查询到表某个时间字段的最大,按照某个字段进行分组,并返回结果。 注意事项: - 将"表名"替换为实际的表名。 - 将"时间字段"和"分组字段"替换为实际的字段名。 - 如果要增加其他字段的输出,可在SELECT语句添加。 例如,查询一个名为"orders"的表"order_date"字段的最大日期,按照"customer_id"字段进行分组: ```sql SELECT customer_id, MAX(order_date) AS 最大日期 FROM orders GROUP BY customer_id; ``` 这将返回一个结果集,其每个分组的"customer_id"和对应的最大日期。 ### 回答3: 可以通过以下步骤使用MySQL查询某个时间最大并按照某个字段分组: 1. 使用MAX()函数找到表某个时间字段的最大。例如,如果时间字段名为"timestamp",则可以使用以下语句获取最大: SELECT MAX(timestamp) FROM 表名; 2. 使用GROUP BY子句按照某个字段分组。假设要按照字段"category"进行分组,则可以在查询语句添加以下代码: GROUP BY category; 所以,最终的查询语句将类似于: SELECT MAX(timestamp) FROM 表名 GROUP BY category; 以上查询语句将返回每个不同类别的时间字段的最大,并按照类别分组。 注意:请将"表名"和"timestamp"字段名替换为实际使用的表和字段名称。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值