持续创作,加速成长!这是我参与「掘金日新计划 · 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='商品信息表'
复制代码
第一种
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;
复制代码
效果:
第一种方法用到子查询和group by
,我们先看子查询 ,执行一下
SELECT
GROUP_CONCAT( id ORDER BY id DESC ) AS ids
FROM
goods
GROUP BY
category_id
复制代码
先通过category_id
进行分组,我的数据有三个类别,所以有三行,每一行中都是逗号间隔的 id 这个是GROUP_CONCAT
的作用,他会将一组中某列的数据用逗号分隔组合在一起,用法就是 GROUP_CONCAT( id )
后面的 ORDER BY
很好理解就是排序的,最新的id放到前边,这里也可以加其他操作。
现在我们获取的是最新的3条,如果需要获取先存的3条将排序改一下即可。 GROUP_CONCAT( id ORDER BY id ASC )
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个就是我们需要的数据。
性能
看一下性能分析
第二种
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;
复制代码
性能
第三种
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;
复制代码
第三种是对第二种的优化,我们说一下具体的思路。 其实就是自己和自己连接,条件就是第一个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
复制代码
现在我们去掉 where 条件
为什么要加这么一个判断条件呢,你想我们想获取最新的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条,将>=
换成 <=
性能
好了,我只是举了一个例子,不一定必须要用id,根据你的需求可以换成其他的,弄明白这个过程就ok了。