ORDER BY RAND() 在 MySQL 中可实现随机排序但性能差,全表扫描导致大数据量时变慢;ROW_NUMBER() OVER (ORDER BY RAND()) 在 MySQL 8.0+/PostgreSQL 中每行独立计算 RAND(),SQL Server 中则只算一次致排序失效;稳定随机编号应改用哈希函数;大表推荐用 COUNT+OFFSET 或 TABLESAMPLE 替代。

ORDER BY RAND() 在 MySQL 中直接生效但有性能陷阱
MySQL 支持 RAND() 作为 ORDER BY 的表达式,能快速实现随机排序,比如:
SELECT * FROM users ORDER BY RAND() LIMIT 10;
但它会在排序前为每一行计算一次 RAND(),导致全表扫描+临时文件排序,数据量超过几万行时响应明显变慢。尤其在没有合适索引、又带 WHERE 条件的查询中,优化器常放弃使用索引而走全表。
ROW_NUMBER() OVER (ORDER BY RAND()) 在 PostgreSQL / SQL Server / MySQL 8.0+ 可用但行为不同
窗口函数本身不“支持” RAND() 作为排序依据——它只是把 RAND() 当作普通表达式求值。关键在于:不同数据库对窗口函数内 RAND() 的求值时机不一致:
- PostgreSQL:每行调用一次
RAND(),结果真正随机 - MySQL 8.0+:同样每行独立计算,可用
- SQL Server:
RAND()在查询开始时只算一次,所有行得到相同值 → 排序失效,必须改用NEWID()或CHECKSUM(NEWID())
正确写法示例(MySQL/PG):
SELECT *, ROW_NUMBER() OVER (ORDER BY RAND()) AS rn FROM products;
需要稳定随机编号?别依赖 RAND(),改用 HASH + MOD
如果目标是“每次查询结果顺序不同,但同一行的编号固定”(比如 A/B 测试分桶),RAND() 不满足要求——它每次执行都变。此时应基于某列(如 id)做确定性哈希:
- MySQL:用
MD5(id)截取部分字符转数字,或直接CONV(SUBSTR(MD5(id), 1, 8), 16, 10) - PostgreSQL:用
hashtext(id::text)或abs(('x'||substr(md5(id::text),1,8))::bit(32)::int) - 再配合
ROW_NUMBER() OVER (ORDER BY hash_value)得到可复现的“伪随机”序号
大表慎用随机排序,优先考虑抽样替代方案
真正需要“从千万级用户里随机取 100 条”时,ORDER BY RAND() 几乎必然超时。更可行的做法是:
- 先用
SELECT COUNT(*)拿总数N - 生成 100 个
[0, N)范围内的随机整数(应用层或存储过程) - 对每个随机数
r,执行SELECT ... LIMIT 1 OFFSET r(需主键有序且无删除空洞,否则需用 ID 范围查找) - 或用伯努利抽样(PostgreSQL 的
TABLESAMPLE SYSTEM (0.1))
随机编号不是目的,拿到代表性样本才是。绕过 RAND() 排序,往往才是生产环境里真正能跑通的路。










