Mysql获取每个分类下的前几条数据

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

引言

假如我现在在写一个商城网站,现在我们有一个需求,需要展示每个商品分类的最新3条商品信息,我们需要从数据库中获取每个分类的最新3条数据,应该怎么做呢,下面我会介绍一下几种方法并解释一下为什么可以做到。

数据示例

数据结构示例:

CREATE TABLE `goods` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '商品ID',
  `name` varchar(100) CHARACTER SET utf8 NOT NULL COMMENT '商品名称',
  `category_id` int(11) NOT NULL COMMENT '类别id',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `goods_category_id_IDX` (`category_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb4 COMMENT='商品信息表'
复制代码

image-1655036923162

第一种

ok,下面开始第一种方式

SQL

SELECT				
	a.* 			
FROM				
	goods AS a,			
	( SELECT GROUP_CONCAT( id ORDER BY id DESC ) AS ids FROM goods GROUP BY category_id ) AS b 			
WHERE				
	FIND_IN_SET( a.id, b.ids ) BETWEEN 1 AND 3 			
ORDER BY				
	a.category_id ASC,			
	a.id DESC;			
复制代码

效果:

image-1655036976464

第一种方法用到子查询和group by,我们先看子查询 ,执行一下

SELECT
	GROUP_CONCAT( id ORDER BY id DESC ) AS ids 
FROM
	goods 
GROUP BY
	category_id
复制代码

image-1655037269437 先通过category_id 进行分组,我的数据有三个类别,所以有三行,每一行中都是逗号间隔的 id 这个是GROUP_CONCAT的作用,他会将一组中某列的数据用逗号分隔组合在一起,用法就是 GROUP_CONCAT( id ) 后面的 ORDER BY 很好理解就是排序的,最新的id放到前边,这里也可以加其他操作。

现在我们获取的是最新的3条,如果需要获取先存的3条将排序改一下即可。 GROUP_CONCAT( id ORDER BY id ASC ) image-1655037758326

OK,现在我们有了每个类别下的id,接下来通过FIND_IN_SET( a.id, b.ids ) 函数来获取3条数据。

FIND_IN_SET( str, strList ) 函数可以返回 str 在 strList中的位置,找不到返回0 例如:SELECT FIND_IN_SET('b', 'a,b,c,d'); 结果:2

接着我们加了一个 BETWEEN 1 AND 3 也就是说我们只要 他返回1,2,3 时候的数据,因为我们之前排过序,所有前3个就是我们需要的数据。

性能

看一下性能分析 image-1655038622128

第二种

SQL

SELECT
	a.* 
FROM
	goods AS a 
WHERE
	( SELECT COUNT(*) FROM goods AS b WHERE b.category_id = a.category_id AND b.id >= a.id ) <= 3 
ORDER BY
	a.category_id ASC,
	a.id DESC;
复制代码

image-1655038960675

性能

image-1655039309661

第三种

SQL

二三种思路差不多,放在一起说

SELECT
	a.*,
	count(*) AS num 
FROM
	goods a
	INNER JOIN goods b ON a.category_id = b.category_id 
WHERE
	b.id >= a.id 
GROUP BY
	a.id 
HAVING
	num <= 3 
ORDER BY
	a.category_id ASC,
	a.id DESC;
复制代码

image-1655039368674

第三种是对第二种的优化,我们说一下具体的思路。 其实就是自己和自己连接,条件就是第一个a.category_id = b.category_id 类别id相等。

第二个条件b.id >= a.id,什么意思呢,就是我只跟id 大于等于我的记录组合,来看一个例子。

比如说现在a表的id是3,我只跟大于等于我的id组合,那么就是

3 3
3 4
3 5
复制代码

我们单独运行一下连接,看一下结果,只看这个id为12的比较直观

SELECT
	* 
FROM
	goods a
	INNER JOIN goods b ON a.category_id = b.category_id 
WHERE
	b.id >= a.id
复制代码

image-1655040519135

现在我们去掉 where 条件

image-1655040706217

为什么要加这么一个判断条件呢,你想我们想获取最新的3条数据,我们的id是递增的所以新的肯定比旧的大,那么最新的id连接之后会有几条记录,1条对不对,倒数第二新的呢,2条对不对,他后边新增了几条连接后就会有几条,这个数量是不是就是我们想要的顺序。

ok,接下来我们只需要统计一下有几条记录,就知道他是第几个了。(这里是按照递减,倒着排)根据 id group by 分组,count(*) 就可以得到数量了,也就是我们要的顺序了, 然后having 筛选 小于等于3,也就是前三个。

注意:如果 Mysql 本版本在 5.6 以下 ,这里的 having 不能使用 num 这个别名,需要直接使用 聚合函数 having count(*) ,原因是查询时 select 是 在 having 之后执行的,5.6 之后 Group by 进行了扩展,可以使用select中的别名。

如果要按照新增先后顺序和获取前3条,将>= 换成 <= image-1655041683269

性能

image-1655041537756

好了,我只是举了一个例子,不一定必须要用id,根据你的需求可以换成其他的,弄明白这个过程就ok了。

猜你喜欢

转载自juejin.im/post/7108352120856215589