数据库表中的随机记录(T-SQL)


85

有没有一种简洁的方法可以从sql server表中检索随机记录?

我想将单元测试数据随机化,因此正在寻找一种从表中选择随机ID的简单方法。用英语来说,选择将是“从表中选择一个ID,其中ID是表中最低ID和表中最高ID之间的随机数”。

我无法找到一种方法,而不必运行查询,测试null值,然后如果为null则重新运行。

有想法吗?



2
您确定要采用这种方法吗?单元测试数据不应是随机的-实际上,无论执行单元测试多少次,都应保证获得相同的结果。拥有随机数据可能会违反单元测试的基本原理。
发挥

@Mesh上面的链接不再有效。
罗伯·西弗斯

Answers:


145

有没有一种简洁的方法可以从sql server表中检索随机记录?

SELECT TOP 1 * FROM table ORDER BY NEWID()

说明

NEWID()为每一行生成一个A ,然后对该表进行排序。返回第一个记录(即具有“最低” GUID的记录)。

笔记

  1. 自第四版以来,GUID生成为伪随机数:

    版本4 UUID用于根据真实随机或伪随机数生成UUID。

    算法如下:

    • 将clock_seq_hi_and_reserved的两个最高有效位(位6和7)分别设置为零和一。
    • 将time_hi_and_version字段的四个最高有效位(第12至15位)设置为4.1.3节中的4位版本号。
    • 将所有其他位设置为随机(或伪随机)选择的值。

    通用唯一标识符(UUID)URN命名空间-RFC 4122

  2. 替代方案SELECT TOP 1 * FROM table ORDER BY RAND()将不会像人们想象的那样起作用。RAND()每个查询返回一个单一值,因此所有行将共享相同的值。

  3. 尽管GUID值是伪随机的,但对于要求更高的应用程序,您将需要更好的PRNG。

  4. 对于大约1,000,000行,典型性能不到10秒-当然取决于系统。请注意,不可能达到索引,因此性能将受到相对限制。


正是我想要的。我觉得这比我做的要简单。
杰里米

1
您假设NEWID产生伪随机值。它很有可能会产生顺序值。NEWID只会产生唯一的值。但是,RAND会生成伪随机值。
Skizz

我在具有1,671,145行的高索引表上运行它,并且需要7秒才能返回。该表也是非常理想的-它实际上是数据库的核心,因此已得到照顾。
汤姆·里特

@ÂviewAnew。对于没有(也不能)命中索引的选择,有160万行7秒的记录也不错。
Sklivvz

7
@Skizz,兰德不能那样工作。在SELECT之前生成一个SINGLE随机值。因此,如果您尝试“ SELECT TOP 10 RAND()...”,则总会得到相同的值
Sklivvz

27

在较大的表上,您也可以使用TABLESAMPLE它来避免扫描整个表。

SELECT  TOP 1 *
FROM YourTable
TABLESAMPLE (1000 ROWS)
ORDER BY NEWID()

ORDER BY NEWID仍然需要避免首次出现在数据页上只返回行。

对于表的大小和定义,需要仔细选择要使用的数字,如果不返回任何行,则可以考虑重试逻辑。这背后的数学和为什么技术并不适合于小表是这里讨论


我在Microsoft网站上发现了此问题:当满足以下任一条件时,可以使用TABLESAMPLE从大表中快速返回样本:样本不必是单个行级别的真正随机样本。该表的各个页面上的行与同一页面上的其他行不相关。
Mark Entingh '17

1
@MarkEntingh-在TOP 1同一情况下,同一页面上的行是否相关并不重要。您只选择其中之一。
马丁·史密斯

9

还尝试使用您的方法来获得MIN(Id)和MAX(Id)之间的随机ID,然后

SELECT TOP 1 * FROM table WHERE Id >= @yourrandomid

它总是会让你排成一行。


2
-1,这仅在最小和最大之间没有丢失的ID时才有效。如果删除了一个,则随机函数会生成相同的ID,您将获得零记录。
尼尔N

6
@Neil,不是真的-如果缺少ID,它将带给您第一行ID大于随机数的ID。这里的问题是每一行出现的概率不是恒定的。但话又说回来,在大多数情况下就足够了。
Sklivvz 2011年

1
+1。对于应该达到足够好的不同值的单元测试-如果您需要一个真正的随机数,那么这就是另外一回事了。但是在OP上下文中,它应该足够好。
TomTom 2012年

7

如果要选择大数据,我所知道的最好方法是:

SELECT * FROM Table1
WHERE (ABS(CAST(
    (BINARY_CHECKSUM
    (keycol1, NEWID())) as int))
    % 100) < 10

资料来源:MSDN


我不确定,但是我认为使用RAND()而不是NEWID()生成真正的随机数可能会更好,因为在选择过程中使用NEWID()会带来不利影响。
QMaster

我尝试使用具有确切记录数而不是基础百分比的此方法,我使用扩展选择范围并使用TOP n进行限制,是否有任何建议?
QMaster

在这种情况下,我发现了另一个问题,如果您使用group by,则总是会得到相同顺序的随机选择的行,因此在小表中,@ skilvvz方法似乎是最合适的。
QMaster

0

我一直在尝试改进尝试过的方法,并且发现了这篇文章。我知道它很旧,但未列出此方法。我正在创建和应用测试数据;这显示了用@st调用的SP中的“地址”方法(两个字符状态)

Create Table ##TmpAddress (id Int Identity(1,1), street VarChar(50), city VarChar(50), st VarChar(2), zip VarChar(5))
Insert Into ##TmpAddress(street, city, st, zip)
Select street, city, st, zip 
From tbl_Address (NOLOCK)
Where st = @st


-- unseeded RAND() will return the same number when called in rapid succession so
-- here, I seed it with a guaranteed different number each time. @@ROWCOUNT is the count from the most recent table operation.

Set @csr = Ceiling(RAND(convert(varbinary, newid())) * @@ROWCOUNT)

Select street, city, st, Right(('00000' + ltrim(zip)),5) As zip
From ##tmpAddress (NOLOCK)
Where id = @csr

0

如果您确实希望随机获取单个行的样本,请修改查询以随机过滤掉行,而不是使用TABLESAMPLE。例如,以下查询使用NEWID函数返回Sales.SalesOrderDetail表的大约百分之一的行:

SELECT * FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), SalesOrderID) & 0x7fffffff AS float)
/ CAST (0x7fffffff AS int)

SalesOrderID列包含在CHECKSUM表达式中,因此NEWID()每行评估一次以实现每行采样。表达式CAST(CHECKSUM(NEWID(),SalesOrderID)&0x7fffffff AS float / CAST(0x7fffffff AS int)的计算结果为0到1之间的随机浮点值。”

来源:http//technet.microsoft.com/zh-CN/library/ms189108(v = sql.105).aspx

下面进一步解释:

这是如何运作的?让我们分解WHERE子句并对其进行解释。

CHECKSUM函数正在计算列表中各项的校验和。关于是否甚至需要SalesOrderID的争论,因为NEWID()是一个返回新随机GUID的函数,因此,在任何情况下,将随机数乘以常数都会导致随机。确实,排除SalesOrderID似乎没有什么区别。如果您是一位敏锐的统计学家,并且有理由将其包括在内,请使用下面的评论部分,让我知道我为什么做错了!

CHECKSUM函数返回一个VARBINARY。使用0x7fffffff执行按位与运算(相当于二进制(111111111 ...)),将产生一个十进制值,该值实际上表示0和1的随机字符串。除以系数0x7fffffff会将该十进制数字有效地归一化为介于0和1之间的数字。然后要确定每一行是否值得最终结果集中包括,使用阈值1 / x(在这种情况下为0.01),其中x是要作为样本检索的数据的百分比。

来源:https : //www.mssqltips.com/sqlservertip/3157/different-ways-to-get-random-data-for-sql-server-data-sampling

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.