emmmm,最近又写前端又写后端,有点忙,好久没写博客,今天来更新一波。
需求:根据商城展示的商品,从数据库中随机查询出相同种类的商品30个,作为随机推介给用户。首先来认识两句sql:
SELECT FLOOR(RAND()*100)
- 这句sql会返回从0到100的随机数一个,floor的作用是返回舍弃小数点的整数,比如4.5则返回4,相对的是ceil,4.5则返回5。
SELECT * FROM tablename ORDER BY RAND() LIMIT 1
- 这句sql可能是百度上教你最多的,也是官方的标准sql语句,rand()就是mysql的随机返回函数。
看完上面,你是不是觉得好像有了2就完事了,没必要写这篇博客了呢?确实,我一开始也是这么写的,贴代码~
SELECT
*
FROM
t_merchandise_info
WHERE
goods_type_name = #{typeName} //如果有种类id,这里用id更好
ORDER BY
RAND()
LIMIT 30;
好啦,写完了,然后页面上一执行…等了一年都没出结果,咋回事?拿去navicat执行看看,然后发现了问题所在!
测试库中的商品表有10万条数据,然后返回的时间长达13秒,emmmm这也太慢了,后面去百度,mysql官方其实也不推介这个函数,他执行的时候会查询所有的表,然后后面原理很复杂,我们只需要知道直接执行效率极低就行了。从用户的角度显然不可能等你这10几秒呀,行吧,那就优化呗,根据百度提示,试下用上面示例的语句1,然后套子函数试试,然后优化成了这样:
SELECT
*
FROM
`t_merchandise_info`
WHERE
id >= (
SELECT
floor(
RAND() * ( SELECT MAX( id ) FROM `t_merchandise_info` )))
ORDER BY
id
LIMIT 30;
百度了好多都没有解释,不知道大家是不是都看不太懂为啥外层是"id>=",我这里解释一下:子查询查到一个随机id,然后这个id作为一个坐标轴,如果是>=的话就会从这个id处向右(往大取),连续取30个商品返回,注意是连续取的30个商品,某种程度上其实是一种伪随机。同理,如果改成“<=”,那么就会从这个id处往左连续取30个商品。(这里要求id必须为数字,且sql不太完善,需要把最小值的偏移量去掉,也就是准确算出"rand()"里的范围,因为默认id从1开始,就不细说了)
这样写,貌似就可以了,查询时间只有0.6秒!可是,分类呢?按照要求,我应该还加一个where goods_type_name = #{typeName}的,但是这个限制条件,无论是放在子句里,还是主句里,都不生效,仍然会查询全表。加入种类是“奶粉”,放在子句里,确实会使MAX(id)这个的商品的类目限制为“奶粉”,但也只是这一个品,其他29个品都不受限制。如果放在外面,返回的30个结果里就筛选出了10个“奶粉”品,也不符合要求,那咋办呢?
最终,没想到还是根据sql优化的基本操作来解决了这个问题(大家一定要多了解sql优化呀),就是重新定义一个实体类,只返回你需要的字段,不要返回所有,绕了一圈还是回来了…然后用实体类去接收,速度也还可以接受,这样就可以了。
SELECT
name, goods_img01,id
FROM
t_merchandise_info t1
WHERE
goods_type_name ='奶粉'
ORDER BY
RAND()
LIMIT 30;