使用MySQL从20万条数据中通过筛选随机取出1条数据的方法

因为业务需要,要求在一个where筛选完的数据集中随机取出1条数据。

经典的方法

1
SELECT * FROM table where 条件 ORDER BY RAND() limit 1;

适用入门级应用,怎么这样说呢?

因为数据量小时,倒没多大问题,由于会进行全表扫描,当数据量渐渐巨型时,查询时间会相当变态。

本地环境运行需时在0.276s左右。

百度出来别人验证过,性能高效的方法是

1
2
3
4
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;

加上我们需要的条件

1
2
3
4
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 and 条件1 and 条件2 and 条件3
ORDER BY t1.id LIMIT 1;

我们下面就来测试一下
生产虚拟数据
先生成20万条虚拟数据,要随机生成,保证数据的均匀分布。

跑一下
平均只用0.006s左右

统计符合条件的数目

1
SELECT COUNT(*) FROM `table` WHERE 条件1 and 条件2 and 条件3;

符合条件的数据有8340条,占比0.0417。

数据校验
运行8000次
校验通过:0.999875, 检验不通过:0.000125
计算了一下,是1条。

运行了两次程序,结果依旧,原因暂未查明。

数据重复性测试

运行出现1次2次3次4次5次6次7次8次9次10次11次12次13次14次
10000.79700.17600.027000000000000
20000.63550.2650.08250.0140.0025000000000
30000.53670.28530.12900.03070.01000.008000000000
40000.46030.29000.13880.05900.03130.01500.00350.0020000000
50000.38440.30000.16320.08720.0420.01320.00840.0016000000
60000.34050.28330.19650.09670.05000.01600.00700.00670.003000000
70000.29900.27000.17010.10000.07290.04970.02300.00690.00390.00430000
80000.2660.25930.19350.10950.07190.04650.02630.0140.00340.00250.00280.004500
由此来看,随机性还是挺不错的,多数数据是出现在前段,这个方法可以使用。

注意:在max,min里面的语句不能再加入where,加入后发现查询明显减慢,经EXPLAIN分析是会导致一条或多条select_type为SUBQUERY进行全表扫描。

美化SQL:

1
2
3
4
5
6
7
8
9
10
11
12
SELECT * FROM `T_UserDefaultProfile` AS t1 
JOIN (
SELECT ROUND(
RAND() * (
(SELECT MAX(F_UdpID) FROM `T_UserDefaultProfile`)-(SELECT MIN(F_UdpID) FROM `T_UserDefaultProfile`)
) + (SELECT MIN(F_UdpID) FROM `T_UserDefaultProfile`)
) AS F_UdpID
) AS t2
WHERE t1.F_UdpID >= t2.F_UdpID and 1=1 and 2=2 and 3=3
ORDER BY t1.F_UdpID LIMIT 1;

SELECT ROUND(1,10)

————————————————
原文链接:https://blog.csdn.net/Wai_Leung/article/details/88318905