【问题标题】:Random record from a database table (T-SQL)数据库表中的随机记录 (T-SQL)
【发布时间】:2008-10-10 13:45:45
【问题描述】:

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

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

如果不运行查询,测试 null 值,然后如果为 null,则重新运行。

想法?

【问题讨论】:

  • 您确定要采用这种方法吗?单元测试数据不应该是随机的——事实上,无论你执行多少次单元测试,都应该保证得到相同的结果。拥有随机数据可能会违反单元测试的基本原则。
  • 以上来自@Mesh 的链接已失效。

标签: sql-server tsql random


【解决方案1】:

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

是的

SELECT TOP 1 * FROM table ORDER BY NEWID()

说明

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

注意事项

  1. GUID 从第四版开始生成为伪随机数:

    版本 4 UUID 用于从真正随机或 伪随机数。

    算法如下:

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

    A Universally Unique IDentifier (UUID) URN Namespace - RFC 4122

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

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

  4. 对于大约 1,000,000 行,典型性能不到 10 秒 — 当然取决于系统。请注意,不可能命中索引,因此性能会相对有限。

【讨论】:

  • 正是我想要的。我有一种感觉,它比我做的要简单。
  • 您假设 NEWID 产生伪随机值。它很有可能会产生顺序值。 NEWID 只产生唯一值。然而,RAND 会产生伪随机值。
  • 我在一个有 1,671,145 行的大量索引表上运行它,需要 7 秒才能返回。该表也非常理想 - 它实际上是我们数据库的核心,因此得到了妥善处理。
  • @ÂviewAnew。 160 万行和 7 秒的选择没有(也不能)命中索引还不错。
  • @Skizz,rand 不能那样工作。在 SELECT 之前生成一个 SINGLE 随机值。因此,如果您尝试“SELECT TOP 10 RAND()...”,您总是会得到相同的值
【解决方案2】:

也尝试你的方法在 MIN(Id) 和 MAX(Id) 之间获取一个随机 Id,然后

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

它总是会让你获得一排。

【讨论】:

  • -1,这仅在最小值和最大值之间没有缺失 ID 时才有效。如果删除了一个,那么随机函数会生成相同的 ID,您将得到零记录。
  • @Neil,并非如此 - 如果缺少 Id,它将为您提供 Id 大于随机数的第一行。这里的问题是每一行出来的概率不是恒定的。但话又说回来,这在大多数情况下就足够了。
  • +1。对于应该达到足够好的不同值的单元测试 - 如果您需要一个真正的随机数,那么这是另一回事。但在 OP 上下文中,它应该已经足够好了。
【解决方案3】:

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

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

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

需要根据表的大小和定义仔细选择要使用的数字,如果没有返回行,您可以考虑重试逻辑。这背后的数学原理以及为什么该技术不适合小表是discussed here

【讨论】:

  • 我在微软的网站上发现了这个:当满足以下任一条件时,您可以使用 TABLESAMPLE 从大表中快速返回样本:样本不必是真正随机的样本各个行的级别。表的各个页面上的行与同一页面上的其他行不相关。
  • @MarkEntingh - 对于TOP 1,同一页面上的行是否相关并不重要。您只选择其中之一。
【解决方案4】:

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

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

来源:MSDN

【讨论】:

  • 我不确定,但我认为使用 RAND() 而不是 NEWID() 生成真正的随机数可能会更好,因为在选择过程中使用 NEWID() 的缺点。
  • 我尝试使用这种方法,使用精确的记录数而不是百分比基数,我通过扩大选择范围并限制 TOP n 来做到这一点,有什么建议吗?
  • 我发现了这个场景的另一个问题,如果你使用 group by 你会得到随机选择的行的相同顺序,所以在小表中@skilvvz 方法似乎是最合适的。
【解决方案5】:

我正在寻求改进我尝试过的方法并遇到了这篇文章。我意识到它很旧,但没有列出这种方法。我正在创建和应用测试数据;这显示了用@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

【讨论】:

    【解决方案6】:

    如果您真的想要单个行的随机样本,请修改您的查询以随机过滤掉行,而不是使用 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/en-us/library/ms189108(v=sql.105).aspx

    这将在下面进一步解释:

    这是如何工作的?我们把WHERE子句拆分出来解释一下。

    CHECKSUM 函数正在计算 列表。是否需要 SalesOrderID 是有争议的,因为 NEWID() 是一个返回一个新的随机 GUID 的函数,因此乘以一个 在任何情况下,常数的随机数字都应该导致随机。 事实上,排除 SalesOrderID 似乎没有什么区别。如果你是 一位敏锐的统计学家,并且可以证明将其包含在内,请使用 下面的 cmets 部分,让我知道为什么我错了!

    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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-10
      • 2011-07-02
      • 2017-09-01
      相关资源
      最近更新 更多