NEWID()能实现随机排序是因为每次调用生成唯一GUID,SQL Server在ORDER BY中逐行重算,使排序键不可预测;它是SQL Server中替代RAND()的轻量方案,但性能差、不可分页复现。

NEWID() 为什么能实现随机排序
因为 NEWID() 每次调用都生成一个全新的、全局唯一的 GUID,SQL Server 在 ORDER BY 中对每一行重新计算该函数,导致排序键完全不可预测。这和 ORDER BY RAND()(MySQL)或 ORDER BY RANDOM()(PostgreSQL)逻辑一致,但 SQL Server 没有内置的标量随机数函数,NEWID() 是最轻量且被广泛接受的替代方案。
直接在 ORDER BY 中使用 NEWID() 的写法
这是最常用也最安全的方式,适用于中小数据量的随机抽样或展示场景:
SELECT TOP 10 * FROM Users ORDER BY NEWID();
注意以下几点:
-
TOP n必须配合ORDER BY NEWID()才有效;只写ORDER BY NEWID()会全表扫描并为每行生成新 GUID,性能随表增大急剧下降 - 不能在视图或内联表值函数中直接引用
NEWID()作排序——SQL Server 会报错Invalid use of a side-effecting operator 'newid' in a function - 如果查询带
WHERE条件,先过滤再排序更高效;避免写成ORDER BY (SELECT NEWID())这类无意义嵌套
NEWID() 在分页随机查询中的陷阱
想“每次翻页都随机,且不重复”?NEWID() 本身不支持可重现的随机序列,所以以下写法是错的:
SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY NEWID()) AS rn FROM Orders) t WHERE rn BETWEEN 11 AND 20;
问题在于:两次执行该语句,ROW_NUMBER() 分配的顺序完全不同,第二页内容既不固定,也不和第一页正交。真实需求通常需要:
- 一次性生成随机序号并持久化(如加临时列或存入 #temp 表),再分页查它
- 用
CHECKSUM(NEWID(), [ID])或ABS(CHECKSUM(NEWID())) % N做确定性哈希(仅适合简单取模抽样) - 业务层维护种子 + 算法生成伪随机序列,数据库只负责按 ID 查——这才是可控的“分页随机”
性能影响与替代思路
对百万级以上表,ORDER BY NEWID() 会触发 SORT 运算符,内存占用高、IO 大、执行计划不稳定。实测中,100 万行排序耗时常超 3 秒,且无法利用任何索引。
如果只是要“近似随机”的少量样本,可以考虑:
- 用
TABLESAMPLE SYSTEM (1)(系统采样,快但不均匀,不保证行数精确) - 先用
WHERE ABS(CHECKSUM(NEWID())) % 100 = 0快速筛出约 1% 行,再ORDER BY NEWID()小结果集排序 - 建一个带
random_sort_key AS CONVERT(BINARY(16), NEWID()) PERSISTED的计算列并建索引——适合频繁随机查且能接受首次写开销的场景
真正难的不是怎么写那行 ORDER BY NEWID(),而是想清楚:你到底要的是“每次不同”的随机,还是“可复现/可分页/可索引”的随机。后者几乎必然要跳出纯 SQL 思维。

















