41

我知道如果我运行这个查询

select top 100 * from mytable order by newid()

它将从我的表中获得 100 条随机记录。

但是,我对它的工作原理有点困惑,因为我没有newid()select列表中看到。有人可以解释吗?这里有什么特别的newid()吗?

4

6 回答 6

34

我知道 NewID() 做了什么,我只是想了解它对随机选择有何帮助。是不是 (1) select 语句将从 mytable 中选择所有内容,(2) 为所选的每一行添加由 NewID() 生成的唯一标识符,(3) 按此唯一标识符对行进行排序,以及 (4) 选择顶部排序列表中的 100 个?

是的。这几乎是完全正确的(除了它不一定需要对所有行进行排序)。您可以通过查看实际执行计划来验证这一点。

SELECT TOP 100 * 
FROM master..spt_values 
ORDER BY NEWID()

计算标量运算符NEWID()为每一行添加列(在我的示例查询中的表中为 2506),然后表中的行按此列排序,并选择前 100 个。

SQL Server 实际上不需要从位置 100 向下排序整个集合,因此它使用TOP N排序运算符尝试在内存中执行整个排序操作(对于 的小值N

计划

于 2011-02-12T21:25:51.400 回答
11

一般来说,它是这样工作的:

  • mytable中的所有行都是“循环的”
  • NEWID() 对每一行执行
  • 根据 NEWID() 中的随机数对行进行排序
  • 100 第一行被选中
于 2011-02-12T21:20:56.693 回答
7

这里的关键是 NEWID 函数,它在内存中为每一行生成一个全局唯一标识符 (GUID)。根据定义,GUID 是唯一且相当随机的;因此,当您使用 ORDER BY 子句按该 GUID 排序时,您将获得表中行的随机排序。取前 10%(或您想要的任何百分比)将为您提供表中行的随机抽样。

提出NEWID查询;它很简单,非常适合小桌子。但是,当您将 NEWID 查询用于大型表时,它有一个很大的缺点。ORDER BY 子句使表中的所有行都复制到 tempdb 数据库中,并在其中进行排序。这会导致两个问题: 排序操作通常具有与之相关的高成本。排序可以使用大量的磁盘 I/O,并且可以运行很长时间。在最坏的情况下,tempdb 可能会耗尽空间。在最好的情况下,tempdb 会占用大量磁盘空间,如果没有手动收缩命令,这些空间永远不会被回收。您需要的是一种随机选择行的方法,该方法不会使用 tempdb,并且不会随着表变大而变慢。这是一个关于如何做到这一点的新想法:

SELECT * FROM master..spt_values
  WHERE (ABS(CAST(
  (BINARY_CHECKSUM(*) *
  RAND()) as int)) % 100) < 10

这个查询背后的基本思想是,我们要为表中的每一行生成一个 0 到 99 之间的随机数,然后选择所有那些随机数小于指定百分比值的行。在本例中,我们希望随机选择大约 10% 的行;因此,我们选择随机数小于 10 的所有行。

于 2014-05-07T09:11:44.610 回答
4

正如MSDN所说:

NewID() 创建唯一标识符类型的唯一值。

并且您的表格将按此随机值排序。

于 2011-02-12T18:30:34.833 回答
0

使用select top 100 randid = newid(), * from mytable order by randid 你将被澄清然后..

于 2015-09-01T07:25:21.343 回答
0

我有一个不重要的查询,它使用 newId() 并连接了许多表。它在大约 3 秒内返回大约 10k 行。因此, newId() 在性能还不错且影响不大的情况下可能没问题。但是, newId() 对大表不利。

这是 Brent Ozar 博客的解释 - https://www.brentozar.com/archive/2018/03/get-random-row-large-table/

从上面的链接中,我总结了可用于生成随机 id 的方法。您可以阅读博客了解更多详情。

从大表中获取随机行的 4 种方法:

  1. 方法 1,错误:ORDER BY NEWID() > 性能不佳!
  2. 方法 2,更好但很奇怪:TABLESAMPLE > 许多陷阱 & 并不是真正随机的!
  3. 方法 3,最佳但需要代码:随机主键 > 最快,但不适用于负数。
  4. 方法 4,OFFSET-FETCH (2012+) > 仅对聚集索引正确执行。

更多关于方法 3: 获取表中的顶部 ID 字段,生成一个随机数,然后查找该 ID。对于前 N 行,调用下面的代码 N 次或生成 N 个随机数并在 IN 子句中使用。

/* Get a random number smaller than the table's top ID */
DECLARE @rand BIGINT;
DECLARE @maxid INT = (SELECT MAX(Id) FROM dbo.Users);
SELECT @rand = ABS((CHECKSUM(NEWID()))) % @maxid;

/* Get the first row around that ID */
SELECT TOP 1 *
FROM dbo.Users AS u
WHERE u.Id >= @rand;
于 2020-10-26T21:49:41.217 回答