newid() 能实现随机排序是因为每次调用生成全局唯一且二进制高度离散的 uniqueidentifier,sql server 按其16字节逐字节比较排序,等效于打乱行序;但不可用于索引、视图或确定性表达式,大表慎用,替代方案 tablesample 仅近似随机。

NEWID() 为什么能实现随机排序?
因为 NEWID() 每次调用都生成一个全新的、全局唯一的 uniqueidentifier 值,且该值在二进制层面是高度离散的。SQL Server 对 uniqueidentifier 的排序本质上是按其 16 字节二进制值逐字节比较,所以 ORDER BY NEWID() 实际上是在对一列“不可预测的伪随机字节序列”排序——效果等价于打乱行序。
注意:NEWID() 是非确定性函数,不能用于索引列、计算列或视图定义中;也不能出现在 WHERE 或 HAVING 子句的确定性表达式里(除非上下文允许非确定性)。
ORDER BY NEWID() 最常用的写法
直接在 SELECT 后加 ORDER BY NEWID() 即可获得全表随机排序:
SELECT TOP 100 * FROM Orders ORDER BY NEWID();
常见使用场景包括:抽样测试、前端轮播数据打乱、避免缓存倾斜、生成测试用例等。
-
TOP n必须放在ORDER BY NEWID()前面,否则会先全表排序再取前 n 行,性能极差 - 如果只想要随机 ID 而非整行,写成
SELECT TOP 100 OrderID FROM Orders ORDER BY NEWID() - 不建议在大表(千万级以上)上直接用,因为 SQL Server 会为每一行计算一次
NEWID()并构建完整排序键,内存和 CPU 开销显著
替代方案:TABLESAMPLE 更快但不真正随机
当只需要近似随机抽样且表有聚集索引时,TABLESAMPLE 性能远优于 ORDER BY NEWID():
SELECT * FROM Orders TABLESAMPLE (1 PERCENT);
但它按数据页采样,不是行级均匀随机;结果集大小不固定(即使指定 ROWS),且无法与 ORDER BY 同时使用。
- 适用场景:快速获取大致比例的样本,比如监控、ETL 预览
- 不适用场景:需要严格均匀概率、或必须返回恰好 N 行时
- 若表无聚集索引(堆表),
TABLESAMPLE可能返回空结果或极不稳定
容易被忽略的性能陷阱
在视图或 CTE 中写 ORDER BY NEWID() 并不保证最终结果随机——SQL Server 可能延迟执行或优化掉排序,尤其嵌套多层后。
- 最稳妥做法:把
ORDER BY NEWID()放在最终 SELECT 顶层,且不带 OFFSET/FETCH(它们会强制排序但破坏随机性) - 避免在 JOIN 的子查询里用
ORDER BY NEWID(),可能导致重复计算或计划退化 - 临时表 +
NEWID()列是可控方案:先SELECT *, NEWID() AS rnd INTO #tmp FROM ...,再SELECT * FROM #tmp ORDER BY rnd
真正随机 ≠ 看起来随机。只要没在最终输出阶段触发物理排序,就可能被优化器绕过。











