【问题标题】:Random select is not always returning a single row随机选择并不总是返回单行
【发布时间】:2011-03-03 10:10:29
【问题描述】:

以下(简化的)代码片段的目的是返回一个随机行。 不幸的是,当我们在查询分析器中运行这个片段时,它会返回零到三个结果。

由于我们的输入表正好由具有唯一 ID 的 5 行组成,并且当我们在这个 ID 等于一个随机数的表上执行选择时,我们很难过会有不止一行回来。

注意:除此之外,我们已经尝试将校验和结果转换为整数,但无济于事。

DECLARE @Table TABLE (
  ID INTEGER IDENTITY (1, 1)
  , FK1 INTEGER
)

INSERT INTO @Table
SELECT 1
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5

SELECT  *
FROM    @Table 
WHERE   ID = ABS(CHECKSUM(NEWID())) % 5 + 1

编辑

我们的使用场景如下(请不要评论这样做是否正确。这是权力决定的)

最终,我们必须通过从表本身中随机选择现有权重来创建具有现实值的结果,其中生产者和权重的组合被混淆
然后查询会变成这样(也是RAND不能使用的原因)

SELECT  t.ID
        , FK1 = (SELECT FK1 FROM @Table WHERE ID=ABS(CHECKSUM(NEWID())) % 5 + 1)
FROM    @Table t

因为内部选择可能返回零结果,所以它会返回一个 NULL 值,这又是不可接受的。正是调查了为什么内部选择在零和 x 结果之间返回,这个问题引起了(这甚至是英语吗?)。

回答

让我大开眼界的是ABS(CHECKSUM(NEWID())) % 5 + 1) 被重新评估每一行的简单观察。我的印象是ABS(CHECKSUM(NEWID())) % 5 + 1) 会被评估一次,然后匹配。

感谢大家的回答,慢慢但肯定会引导我更好地理解。

【问题讨论】:

  • 在多个输出行中是否有来自同一行的 FK1 值?
  • 我不认为这是个问题。
  • 顺便说一句 - 我不相信“sproused”是一个词。 'Sprouse' 被用作姓氏,这让我相信它可能是中古英语或更早的一个词,但它的含义显然已经丢失。
  • @Bob Jarvis,请随时编辑和增强问题。我知道我的意思(显然),但我不知道如何翻译。

标签: sql sql-server sql-server-2005 random


【解决方案1】:

试试下面的方法,看看会发生什么:

SELECT  ABS(CHECKSUM(NEWID())) % 5 + 1 AS Number, @Table.*
FROM    @Table 
WHERE   ID = Number

【讨论】:

  • 您提出的查询并没有按原样运行,但它给了我们一些新的尝试。
【解决方案2】:

有点猜测,不确定 SQL 是否以这种方式工作,但 SQL 不会为表中的每一行计算“ABS(CHECKSUM(NEWID())) % 5 + 1”吗?如果是这样,那么每次评估可能会也可能不会返回当前行的 ID 值。

试试这个——首先显式生成随机数,然后匹配那个单一的值:

declare @targetRandom int
set @targetRandom = ABS(CHECKSUM(NEWID())) % 5 + 1

select * from @table where ID = @targetRandom

【讨论】:

  • +1。您和丹尼尔似乎提出了相同的解决方案,但我仍然无法理解为什么选择可以返回超过 1 行。
【解决方案3】:

发生这种情况的原因是 NEWID() 为表中的每一行赋予不同的值。对于每一行,独立于其他行,有五分之一的机会返回。因此,就目前而言,您实际上有 3125 分之一的机会返回所有 5 行!

要查看此内容,请运行以下查询。您会看到每一行都有一个不同的 ID。

SELECT  * , NEWID()
FROM    @Table  

这将修复您的代码:

DECLARE @Id int
SET @Id = ABS(CHECKSUM(NEWID())) % 5 + 1

SELECT  * 
FROM    @Table  
WHERE   ID = @Id

但是,我不确定这是从表中选择单个随机行的最有效方法。

您可能会发现这篇 MSDN 文章很有用:http://msdn.microsoft.com/en-us/library/Aa175776(T-SQL 中的随机抽样)

编辑 1:现在我想一想,这可能是最有效的方法,假设行数保持固定并且 ID 保证是连续的 i>.

EDIT 2:要在用作子查询时达到预期的结果,请像这样使用 TOP 1:

SELECT  t.ID 
        , FK1 = (SELECT TOP 1 FK1 FROM @Table ORDER BY NEWID()) 
FROM    @Table t

【讨论】:

  • 你哪里更快,我写的是同样的例子,所以+1
  • 您的示例适用于单个选择,但根据我们的要求,这将需要一个光标,其中 @Id 始终获得一个新值。我们更喜欢基于集合的方法。我已更新问题以反映这一点。
  • 另外,我的印象是您知道可以返回多个结果,但我不明白。您如何获得返回所有 5 行的 3125 分之一的机会?
  • 在 SELECT 中使用 NEWID() 意味着第一行有五分之一的机会被返回,第二行有五分之一的机会被返回,第 3 行同样如此, 4 & 5. 所以返回所有 5 行的概率是 (1/5)*(1/5)*(1/5)*(1/5)*(1/5)=1/3125 (I '我假设概率是独立的)
  • @Lieven,我添加了一个示例,您可以运行它来帮助解释为什么会出现多行。
【解决方案4】:

或者您可以使用 RAND() 代替 NEWID(),在 MS SQL 中每个查询只评估一次

如果你想使用 CHECKSUM 来获取随机行,这是这样做的。

SELECT TOP 1 *
FROM @Table
ORDER BY CHECKSUM(NEWID())

怎么样?

SELECT  t.ID 
        , FK1 = (SELECT TOP 1 FK1 FROM @Table ORDER BY NEWID()) 
FROM    @Table t 

【讨论】:

  • RAND() 对我们不起作用。我更新了问题以进一步解释我们的使用场景。
  • 我们知道 ORDER BY NEWID()。它对我们有用(顺便说一句+1),但我想了解为什么给定示例返回的行不止一行。
【解决方案5】:

这可能有助于您了解原因。 多次运行查询。 MY_FILTER = ID 多少次?

SELECT  *, ABS(CHECKSUM(NEWID())) % 5 + 1 AS MY_FILTER
FROM    @Table

SELECT  *, ABS(CHECKSUM(NEWID())) % 5 + 1 AS MY_FILTER
FROM    @Table

SELECT  *, ABS(CHECKSUM(NEWID())) % 5 + 1 AS MY_FILTER
FROM    @Table

【讨论】:

  • MY_FILTER 总是等于正好一个 ID。不是每个有问题的行的 ID,但我看不出这会如何在 simple SELECT * FROM @Table WHERE ID = ABS(CHECKSUM(NEWID())) % 5 + 1. 中造成问题
  • WHERE 对每一行进行评估,NEWID 在每次评估时返回不同的值。
  • ...打开了灯。谢谢/
【解决方案6】:

我不知道这对你有多大帮助,但试试这个。我所理解的是,每次执行查询时你都想要一个随机行。

select top 1 newid() as row,ID from @Table order by row

这是逻辑。每次执行查询时,都会为每一行分配一个 newid,并且所有行都是唯一的,您只需使用新生成的唯一 rowid 对它们进行排序。然后你需要做的就是选择最上面的或者你想要的任何东西..

【讨论】:

    猜你喜欢
    • 2013-01-26
    • 2023-04-04
    • 2014-01-21
    • 2021-04-29
    • 1970-01-01
    • 2015-07-09
    • 2022-01-09
    • 1970-01-01
    • 2021-10-18
    相关资源
    最近更新 更多