最近无事,翻了一下以前的博客,发现部分文章现在看起来确实有很大的差异,对于这篇mysql随机查询的文章,现在看起来,问题的确存在。
举个例子,要从tbl_consult表中随机提取一条记录,大家一般的写法就是:
select * from tbl_consult order by rand() limit 1;
执行之后用mysql slow query log看下,发现全表扫了2次,如果这个量非常大,那这说明了问题确实存在。
在官方手册里有这样说过,在order by从句里面不能使用rand()函数,因为这样会导致数据列被多次扫描,意思应该明白了吧,就是说rand()查询的时候会在全表里搜索多次,直到你取到结果数。
有集中查询方法,做一下对比。
1、采用JOIN,查询max(id) * rand()来随机获取数据
SELECT * FROM tbl_consult AS t1 JOIN (select round(rand() * (select max(id) FROM tbl_consult)) AS id) AS t2 WHERE t1.id >= t2.id ORDER BY t1.id ASC LIMIT 1;
但是这样会产生连续的5条记录。解决办法只能是每次查询一条,查询5次。即便如此也值得,因为15万条的表,查询只需要0.01秒不到。
2、下面这句在mysql论坛上人用到
SELECT * FROM tbl_consult WHERE id >= (select floor( max(id) * rand()) FROM tbl_consult ) ORDER BY id LIMIT 1;
执行上面这句发现还是会大量扫描,也不推荐。
3、稍微的改一下之后
SELECT * FROM tbl_consult WHERE id >= (select floor(RAND() * (select max(id) FROM tbl_consult))) ORDER BY id LIMIT 1;
最后,再把语句完善一下,加上MIN(id)的判断。我在最开始测试的时候,就是因为没有加上MIN(id)的判断,结果有一半的时间总是查询到表中的前面几行。
4、完整查询语句是
SELECT * FROM tbl_consult WHERE id >= (select floor( RAND() * ((select max(id) FROM tbl_consult ) - (select min(id) FROM tbl_consult)) + (select min(id) FROM tbl_consult))) ORDER BY id LIMIT 1;
5、再调整一下
SELECT * FROM tbl_consult AS t1 JOIN (select ROUND(RAND() * ((select MAX(id) FROM tbl_consult)-(select MIN(id) FROM tbl_consult))+(select MIN(id) FROM tbl_consult)) AS id) AS t2 WHERE t1.id >= t2.id ORDER BY t1.id LIMIT 1;
看来采用JOIN的语法比直接在WHERE中使用函数效率还要高很多。