Mysql随机查询一条数据,如何保证一定能查询到结果,并且保证查询的结果具有随机性

现在有一个需求:从mysql数据库的一张表中(表名就叫 table )随机获取一条记录,要求获取的任意一条数据都具有随机性,并且能够保证获取到数据。网上的解决方案通常是这样的:

大部分都是采用如下的方法一:

SELECT
	*
FROM
	`table` AS t1
JOIN (
	SELECT
		ROUND(
			RAND() * (SELECT MAX(id) FROM `table`)
		) AS id
) AS t2
WHERE
	t1.id >= t2.id
ORDER BY
	t1.id
LIMIT 1;

或者使用方法二:

SELECT
	*
FROM
	`table`
WHERE
	id >= (
		SELECT
			floor(
				RAND() * (
					(SELECT MAX(id) FROM `table`) - (SELECT MIN(id) FROM `table`)
				) + (SELECT MIN(id) FROM `table`)
			)
	)
ORDER BY
	id
LIMIT 1;

 方法二再优化一下,得到方法三,如下:

SELECT
	*
FROM
	`table` AS t1
JOIN (
	SELECT
		ROUND(
			RAND() * (
				(SELECT MAX(id) FROM `table`) - (SELECT MIN(id) FROM `table`)
			) + (SELECT MIN(id) FROM `table`)
		) AS id
) AS t2
WHERE
	t1.id >= t2.id
ORDER BY
	t1.id
LIMIT 1;

 

       但是有的时候,查询的语句本身就要关联多张表,同时实际查询的条件也会复杂许多,这就导致使用上述方法时,多次查询后的数据整体就不会呈现出太大的随机性,尤其是当使用多个条件后刷选出来的结果中,每条记录的主键并不一定是规律递增,这就可能导致最终返回的是空记录,显然这不是我们想要的。比如使用方法一,假设我where子句中多个条件帅选后的最终结果有五条记录,记录的id分别是18,50,57,67,69。那么假设此时方法一中的这部分 SELECT ROUND(RAND() * (SELECT MAX(id) FROM `table`)) AS id   执行后的结果t2.id=25,那么方法一的sql执行后的结果,恐怕不止一半的概率都是id=50吧,此时所谓的随机性还能体现出来吗。即便使用方法二、方法三的sql可以减少以上情况的发生,但只是减少,并不是避免,况且以上三种方法还会有一定的概率返回空记录。

 

下面介绍一种我自己想到的方法,直接上代码,下面的都是伪代码,大家自行脑补。首先是代码段A:
 

SELECT
	FLOOR(RAND() *(count(t1.id)))
FROM
	`table` t1
WHERE
	条件1
AND 条件2
AND 条件n

 此时,记代码段A执行后结果为countA .

接着请看代码段B:

select t1.* 
from `table` t1
where 条件1 and 条件2 and 条件n 
limit $(countA),1 

 注意:limit函数后面的${}是mybatis中的字符串替换符,其它持久层框架(如hibernate)则可以根据各家框架的语法规范修改,不用纠结

分析:首先代码段A先查询一遍符合条件的总记录数(也就是 count(t1.id) 得到的结果,它表示符合条件的总记录数),假设有20条记录,然后对总记录数进行随机数取整(也就是 FLOOR(RAND() *(count(t1.id)))得到的结果),得到的一定是0-19之间的整数(之所以是0-19,是因为mysql的limit函数的第一个变量的最小值是0,也就是如果是查询第一条记录,那么就是limit(0,1),而不是limit(1,1) )。由于countA参数是[0,19]内的随机整数,这也就意味着代码段B执行后的结果也是随机的。这种方法可能显得有点笨,因为需要查询两次(主要是因为limit 函数后面只能跟常量),但是相比于一次性查询所有符合条件的记录并加载进内存,比如封装到list集合中,然后再使用list.get(随机数) 随机抽取符合条件的记录这种方式,可能要好的多。毕竟,一次性查询成百上千条记录后再加载到内存中,最终只是为了随机抽取一条记录,这就显得有点浪费资源了。当然了,实际业务的sql语句,通常需要连多表并且是多条件查询的,此时务必要保证代码段 A 和代码段B中所使用的表和where子句一致,这个应该不难理解。

 

如果有更好的方法或者发现了什么问题,欢迎赐教;但是说归说,别爆粗口,我没收谁的钱也没有要求谁来点赞,甚至没有要求谁来看,我只是在自己的一亩三分地里写点小文章,记录一些自己曾经走过的路,然后顺便给以后可能和我有同样问题的人提供一些可能的借鉴,同时希望有人提供更好的思路,让我还能再有所提升。素质不好的人,请远离我的评论区,这样你好,我也好,大家都好,谢谢!

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值