NEWID() 能真正随机是因为每次调用生成唯一不可预测的 GUID,SQL Server 对每行分别求值;而 RAND() 在整个查询中只计算一次,导致排序结果固定不变。

直接用 ORDER BY NEWID() 就行,别碰 RAND() —— 它在单条语句里只算一次,抽出来全是固定顺序。
为什么 NEWID() 能真正随机?
因为 NEWID() 每次调用都生成一个全新、唯一、不可预测的 GUID,SQL Server 在 ORDER BY 中对每一行分别求值,相当于给每行贴了个随机标签再排序。而 RAND() 是标量函数,在整个查询执行时只计算一次,所有行看到的都是同一个浮点数,ORDER BY RAND() 实际上等于按常量排序,结果永远不变。
- 验证方式:跑两次
SELECT TOP 5 * FROM sys.objects ORDER BY RAND(),结果顺序完全一致 - 对比验证:跑两次
SELECT TOP 5 * FROM sys.objects ORDER BY NEWID(),大概率顺序不同 -
NEWID()不依赖种子,也不可复现——这是优点也是缺点,测试时若需稳定结果得另加逻辑
抽固定行数:TOP + ORDER BY NEWID() 最常用
要取 exactly N 条随机记录,这是最直白也最可靠的方式。适用于中小表(百万行以内),性能尚可;超大表会全表扫描+临时排序,延迟明显。
- 示例:抽 100 条用户
SELECT TOP 100 * FROM users ORDER BY NEWID() - 注意:不能写成
WHERE RAND() 这类“概率过滤”,它不保证恰好 N 行,且仍受 <code>RAND()单次求值影响 - 如果表有聚集索引且数据物理有序,
NEWID()排序开销会略高,但不影响正确性
大数据量下想提速?试试 TABLESAMPLE
TABLESAMPLE 是 SQL Server 提供的近似随机采样机制,不排序、不全扫,靠页级随机跳过实现高速抽样,但无法精确控制行数,且结果可能有偏差(尤其小表或数据倾斜时)。
- 语法:
SELECT * FROM users TABLESAMPLE (5 PERCENT)—— 抽约 5% 行,实际行数浮动 - 支持
REPEATABLE子句:TABLESAMPLE (5 PERCENT) REPEATABLE (123),相同 seed 下结果可复现 - 不能和
ORDER BY、TOP直接合用;若后续还要取 TOP,得套子查询,但会失去 TABLESAMPLE 的性能优势 - 对内存和 I/O 压力远低于
NEWID(),千万级以上表优先考虑
插入随机测试数据时 CHECKSUM(NEWID()) 更实用
单纯排序抽样用 NEWID() 足够,但构造随机数值字段(如 ID、分数、字符串编号)时,直接用 NEWID() 不方便,得转整数——CHECKSUM(NEWID()) 是标准解法。
- 示例生成 0–99 的随机整数:
ABS(CHECKSUM(NEWID())) % 100 - 避免负数用
ABS();模运算前不加ABS可能出负余数(取决于 CHECKSUM 实现) - 不要用
CAST(NEWID() AS INT)—— 类型不兼容,直接报错 - 批量插入时,每个字段独立调用
NEWID(),比如name = 'user_' + CAST(ABS(CHECKSUM(NEWID())) AS VARCHAR),确保各列随机性不耦合
真正麻烦的地方不在语法,而在规模感知:小表无脑 TOP N ORDER BY NEWID();大表先评估是否接受近似样本,再决定用 TABLESAMPLE 还是加索引+分区优化 NEWID() 查询;需要可复现时,就得自己维护种子并模拟随机逻辑——NEWID() 天然不支持 seed。

















