ORDER BY NEWID() 能随机是因为每行扫描时都生成新GUID,天然无序;但不可乱用,因全表逐行调用致大表性能骤降、内存溢出,且不能用于视图、函数、索引列等场景。

ORDER BY NEWID() 为什么能随机,但不能乱用
因为 NEWID() 每次被 SQL Server 在扫描每一行时调用,都会生成一个全新的 uniqueidentifier,而 GUID 天然无序、不可预测——所以按它排序,结果就是真正随机的。但关键点在于:这个“逐行调用”是强制的,ORDER BY NEWID() 是唯一能触发该行为的写法;写成 SELECT *, NEWID() AS r FROM t ORDER BY r 效果一样,只是多占一列,不推荐。
大表(10 万+ 行)上直接 ORDER BY NEWID() 会卡死
它会强制全表扫描 + 内存中对所有行的 NEWID() 值做排序,Sort 运算符极易溢出到 tempdb,I/O 和内存压力陡增。尤其当表字段宽(含 varchar(max)、xml 等)时,性能断崖式下跌。
- 别在生产环境对百万级表直接跑
SELECT TOP 100 * FROM huge_table ORDER BY NEWID() - 如果必须纯 SQL 抽样,优先走主键范围抽样:先
SELECT MIN(id), MAX(id),应用层生成 N 个随机 ID,再WHERE id IN (…)查——前提是主键基本连续或稀疏可控 - 若主键不连续又必须 SQL 层解决,用 CTE 先抽 ID 再 JOIN:
WITH random_ids AS ( SELECT TOP 1 id FROM your_table ORDER BY NEWID() ) SELECT t.* FROM your_table t INNER JOIN random_ids r ON t.id = r.id;
这能避免把整行数据拉进排序,显著减少内存和 I/O
DISTINCT 和 NEWID() 不能共存于同一层查询
SQL Server 明确报错:SELECT DISTINCT cannot be used with ORDER BY containing a non-aggregate expression。因为 DISTINCT 是逻辑重写阶段的操作,而 NEWID() 是运行时逐行计算的非确定性函数,二者语义冲突。
- 正确做法是分两步:先用子查询或 CTE 去重,外层再
ORDER BY NEWID()SELECT TOP 1 * FROM ( SELECT DISTINCT col1, col2 FROM your_table ) dedup ORDER BY NEWID();
- 别试图加
GROUP BY绕过——除非你真需要聚合逻辑,否则只是徒增开销
哪些地方根本不能用 NEWID()
SQL Server 把它列为「非确定性函数」,在多个上下文中直接禁止使用:
- 视图定义里写
ORDER BY NEWID()→ 报错Msg 457 - 内联表值函数(ITVF)的
RETURN子句中 → 不允许 - 计算列定义 → 不支持
- 索引视图的键列或包含列 → 直接拒绝创建
- 事务中多次执行同一语句,每次结果都不同——它不遵守事务隔离级别,也无法回滚到“上一次随机状态”
实际用的时候,最常被忽略的是:你以为只取 1 行就很快,但只要写了 ORDER BY NEWID(),SQL Server 就得为全表每一行生成并比较 GUID。哪怕最终只返回 TOP 1,代价也没省下来。

















