我知道如果我运行这个查询
select top 100 * from mytable order by newid()
它将从我的表中获得 100 条随机记录。
但是,我对它的工作原理有点困惑,因为我没有newid()
在select
列表中看到。有人可以解释吗?这里有什么特别的newid()
吗?
我知道如果我运行这个查询
select top 100 * from mytable order by newid()
它将从我的表中获得 100 条随机记录。
但是,我对它的工作原理有点困惑,因为我没有newid()
在select
列表中看到。有人可以解释吗?这里有什么特别的newid()
吗?
我知道 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
)
一般来说,它是这样工作的:
这里的关键是 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 的所有行。
使用select top 100 randid = newid(), * from mytable order by randid
你将被澄清然后..
我有一个不重要的查询,它使用 newId() 并连接了许多表。它在大约 3 秒内返回大约 10k 行。因此, newId() 在性能还不错且影响不大的情况下可能没问题。但是, newId() 对大表不利。
这是 Brent Ozar 博客的解释 - https://www.brentozar.com/archive/2018/03/get-random-row-large-table/。
从上面的链接中,我总结了可用于生成随机 id 的方法。您可以阅读博客了解更多详情。
从大表中获取随机行的 4 种方法:
更多关于方法 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;