【转载】MySQL Order By Rand()效率

本文深入探讨了MySQL中OrderByRand()的效率优化方案,通过比较不同随机查询方法,实验证明采用JOIN语法相较于直接在WHERE子句中使用函数更高效。提供了一个完整的查询语句优化案例,有效降低了查询时间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文地址:http://www.phpchina.com/?action-viewnews-itemid-33727

本文详细解说了 MySQL Order By Rand() 效率优化的方案,并给出了优化的思路过程,是篇不可多得的MySQL Order By Rand()效率美文。
最近由于需要大概研究了一下MYSQL的随机抽取实现 方法。举个例子,要从tablename表中随机提取一条记录,大家一般的写法就是:SELECT * FROM tablename ORDER BY RAND() LIMIT 1。

 

但是,后来我查了一下MYSQL的官方手册,里面针对RAND()的提示大概意思就是,在ORDER BY从句里面不能使用RAND()函数,因为这样会导致数据列被多次扫描。但是在MYSQL 3.23版本中,仍然可以通过ORDER BY RAND()来实现随机。

但是真正测试一下才发现这样效率非常低。一个15万余条的库,查询5条数据,居然要8秒以上。查看官方手册,也说rand()放在ORDER BY 子句中会被执行多次,自然效率及很低。

You cannot use a column with RAND() values in an ORDER BY clause, because ORDER BY would evaluate the column multiple times.
搜索Google,网上基本上都是查询max(id) * rand()来随机获取数据。
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 ASC LIMIT 5;
但是这样会产生连续的5条记录。解决办法只能是每次查询一条,查询5次。即便如此也值得,因为15万条的表,查询只需要0.01秒不到。

下面的语句采用的是JOIN,mysql的论坛上有人使用

SELECT *
FROM `table`
WHERE id >= (SELECT FLOOR( MAX(id) * RAND()) FROM `table` )
ORDER BY id LIMIT 1;
我测试了一下,需要0.5秒,速度也不错,但是跟上面的语句还是有很大差距。总觉有什么地方不正常。

 

于是我把语句改写了一下。

SELECT * FROM `table`
WHERE id >= (SELECT floor(RAND() * (SELECT MAX(id) FROM `table`)))  
ORDER BY id LIMIT 1;
这下,效率又提高了,查询时间只有0.01秒

最后,再把语句完善一下,加上MIN(id)的判断。我在最开始测试的时候,就是因为没有加上MIN(id)的判断,结果有一半的时间总是查询到表中的前面几行。
完整查询语句是:

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;
最后在php中对这两个语句进行分别查询10次,
前者花费时间 0.147433 秒
后者花费时间 0.015130 秒
看来采用JOIN的语法比直接在WHERE中使用函数效率还要高很多。

 

参考文献
MySQL Order By索引优化http://www.phpq.net/mysql/mysql-order-by.html
MySQL Order By语法http://www.phpq.net/mysql/mysql-order-by-syntax.html
MySQL Order By Rand()效率http://www.phpq.net/mysql/mysql-order-by-rand.html
MySQL Order By用法http://www.phpq.net/mysql/mysql-order-by-use.html

 

原文地址:http://www.phpchina.com/?action-viewnews-itemid-33727

<think>好的,我现在需要解释一下SQL中的`ORDER BY RAND()`。用户可能之前已经问过关于`LIMIT 1 OFFSET 1`的问题,现在转向随机排序。首先,我应该回忆这个函数的基本用法和作用。 用户可能想知道`ORDER BY RAND()`的功能,比如它是如何实现随机排序的,以及在实际应用中的常见用途,比如随机抽样数据。我需要解释清楚它的工作机制,比如每次执行时如何生成随机数并排序结果。 然后,我需要考虑使用场景。用户提到过随机抽样的例子,可能需要更详细的说明,比如如何结合`LIMIT`来获取随机的一条或多条记录。同时,可能需要举一个具体的例子,比如从用户表中随机选择一名用户,这样更直观。 接下来,注意事项部分很重要。用户可能不了解`ORDER BY RAND()`的性能问题,尤其是在处理大数据量时,因为它需要对所有数据进行随机排序,这会导致性能下降。我需要解释为什么会有这个问题,并提供替代方案,比如使用应用程序端的随机处理或特定数据库的函数。 另外,不同数据库的差异也需要提及。比如MySQL、PostgreSQL和SQLite可能都支持`RAND()`,但其他数据库如SQL Server使用`NEWID()`,而PostgreSQL还可能用`RANDOM()`。需要指出这些差异,帮助用户避免跨数据库时的兼容性问题。 还要注意语法细节,比如在`ORDER BY`子句中使用`RAND()`的正确方式,是否需要别名或者括号。例如,在有些情况下可能需要`ORDER BY RAND()`,而在其他情况下可能需要`RAND()`作为表达式。 用户可能存在的误区需要澄清,比如多次执行`ORDER BY RAND()`可能会导致不同的结果,这在某些情况下可能是期望的,但在需要确定性结果时可能不适用。同时,随机排序的效率问题,尤其是在大表中,可能需要更优的解决方案。 最后,总结部分需要简明扼要,强调`ORDER BY RAND()`的适用场景和局限性,帮助用户权衡使用。可能还需要提到在分页或需要稳定排序时的替代方法,比如使用固定的种子或者预先计算的随机值列。 需要确保回答结构清晰,分点说明,使用示例和注意事项来增强理解。同时,避免使用Markdown格式,保持口语化,但根据用户之前的回复,可能更倾向于结构化且带有代码块和示例的详细解释。需要检查是否有遗漏的重要点,比如安全方面的考虑或者与其他子句的结合使用。 现在整理这些思路,确保覆盖功能、使用场景、示例、注意事项、数据库差异和总结,同时保持逻辑连贯,逐步深入,帮助用户全面理解`ORDER BY RAND()`的作用和最佳实践。</think>#### SQL中的 `ORDER BY RAND()` 详解 --- ##### 1. **基本功能** `ORDER BY RAND()` 是SQL中用于对查询结果进行**随机排序**的子句。其核心逻辑是: - 为每一行生成一个随机数(通常是0到1之间的浮点数)。 - 根据这些随机数对结果集进行排序,从而实现数据的随机排列。 --- ##### 2. **典型使用场景** ###### ① 随机抽样数据 - 从表中随机选取一条记录: ```sql SELECT * FROM users ORDER BY RAND() LIMIT 1; ``` - 随机选取多条记录(如3条): ```sql SELECT * FROM products ORDER BY RAND() LIMIT 3; ``` ###### ② 随机展示内容 - 在网页或应用中随机显示广告、推荐商品、抽奖用户等场景。 ###### ③ 数据去偏分析 - 在数据分析时,通过随机排序避免因固定顺序导致的统计偏差。 --- ##### 3. **具体示例** 假设表 `students` 包含以下数据: | id | name | |----|--------| | 1 | Alice | | 2 | Bob | | 3 | Carol | | 4 | Dave | 执行以下查询: ```sql SELECT * FROM students ORDER BY RAND(); ``` **可能的输出**(每次执行结果不同): | id | name | |----|--------| | 3 | Carol | | 1 | Alice | | 4 | Dave | | 2 | Bob | --- ##### 4. **注意事项** ###### ① **性能问题** - **大表慎用**:`ORDER BY RAND()` 需要对全表数据生成随机数并排序,数据量较大时效率极低。 **优化方案**: - 对已知连续ID的表,使用随机范围查询(如 `WHERE id >= RAND() * MAX(id)`)。 - 预存随机值列:新增一列存储随机数,定期更新并基于此列排序。 - 使用数据库特定函数(如MySQL的 `RAND(N)` 配合种子)。 ###### ② **随机性范围** - 若结合 `LIMIT` 使用,需注意:当表中数据更新时,多次执行可能返回重复结果。 ```sql -- 假设表中新增一行后,可能再次抽到已出现过的数据 SELECT * FROM logs ORDER BY RAND() LIMIT 1; ``` ###### ③ **数据库兼容性** - **MySQL/PostgreSQL/SQLite**:支持 `RAND()`。 - **SQL Server**:使用 `NEWID()` 实现随机排序: ```sql SELECT TOP 1 * FROM users ORDER BY NEWID(); ``` - **Oracle**:需用 `DBMS_RANDOM.VALUE` 生成随机数: ```sql SELECT * FROM employees ORDER BY DBMS_RANDOM.VALUE FETCH FIRST 1 ROW ONLY; ``` --- ##### 5. **替代方案** ###### ① **应用程序端随机化** - 先查询全部ID,在程序代码中随机选取,再按ID查询具体数据: ```sql -- 第一步:获取所有ID SELECT id FROM products; -- 第二步(程序端随机选3个ID) SELECT * FROM products WHERE id IN (3,7,12); ``` ###### ② **使用固定种子** - 若需“可重复的随机”,可指定随机种子(部分数据库支持): ```sql -- MySQL示例(种子为123) SELECT * FROM users ORDER BY RAND(123) LIMIT 1; ``` --- ##### 6. **总结** - **适用场景**:小数据量随机抽样、动态内容展示。 - **避免场景**:大数据表、高频查询、严格性能要求的场景。 - **优化建议**:根据数据库类型选择高效随机方法,或结合程序端处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值