mysql按某字段值分类后查询每类前n条数据

问题描述

最近遇到一个需求,按表中某字段的值分类(注意不是group by分组),再以某字段的值排序,查看某组中的前n条数据。
注意,这里的分类,不能简单理解为group by分组。从问题描述来看,group by分组查看前n条数据是矛盾的。此问题不能简单使用group by 、order by 、limit 解决。

问题场景

在网上搜索相关解答,发现了有很多类似场景:

  1. 电商网站,商品有很多分类,想取出每个分类下面的前五条数据;
  2. 按日期分类,取某天或某月的前n条数据;
  3. 按学生姓名分类,取某学生的前n条数据;

场景模拟

模拟上述问题场景三,创建学生表,并添加数据如下图:
在这里插入图片描述


解决方案

大致有以下三种解决方案,都可以解决这类问题

一、UNION 拼接

如果分类很多,则会非常麻烦(注意UNION和ORDER BY以及LIMIT一起使用时,要用括号将每条查询语句扩起来)

(SELECT * FROM score a WHERE a.student = '张三' ORDER BY a.id LIMIT 2)
UNION
(SELECT * FROM score a WHERE a.student = '李四' ORDER BY a.id LIMIT 2)
UNION
(SELECT * FROM score a WHERE a.student = '王二' ORDER BY a.id LIMIT 2)

二、GROUP_CONCAT()和FIND_IN_SET()函数

原理:子查询按分类字段分组后,使用聚合函数得到每组用逗号分隔的字段值,外层查询看数据的唯一字段值是否能在子查询得到的逗号分隔的字段值里。

需要利用表中的唯一字段,正序、倒序、添加其他条件都比较方便;
其中正序、倒序修改两处ORDER BY即可;
额外添加查询条件仅需在子查询中添加即可。

SELECT 
    a.*
FROM
    score AS a,
    (SELECT 
        GROUP_CONCAT(id ORDER BY id) AS ids
    FROM
        score
    GROUP BY student) AS b
WHERE
    FIND_IN_SET(a.id, b.ids) BETWEEN 1 AND 2
ORDER BY a.student ASC, a.id;
  • GROUP_CONCAT()函数得到每组用逗号分隔的列值,最大长度可以通过group_concat_max_len环境变量设置;
  • FIND_IN_SET()函数返回id在ids中的位置,不存在返回0;

三、使用变量

原理:
@mid用于判断是否是某类的第一条数据;
@row相当于计数器,用于判断是某类的第几条数据;
最内层子查询将两个变量拼接到原表每条数据后。外层子查询通过判断两个变量,给原表数据分类后加上编号。最外层查询依据编号取值。

SELECT
	a.*, b.rownum
FROM
	score a
LEFT JOIN (
	SELECT
		id,
		CASE
	WHEN @mid = student THEN
		@row :=@row + 1
	ELSE
		@row := 1
	END rownum,
	@mid := student mid
FROM
	(
		SELECT
			@row := 0 ,@mid := '',
			c.*
		FROM
			score c
	) t
ORDER BY
	student,
	score DESC
) b ON b.id = a.id
WHERE
	b.rownum <= 3;
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值