随机选择并不总是返回单行

Lie*_*ers 2 sql sql-server random sql-server-2005

遵循(简化)代码片段的意图是返回一个随机行.不幸的是,当我们在查询分析器中运行此片段时,它返回0到3之间的结果.

由于我们的输入表恰好包含5行且具有唯一ID,并且当我们在此表上执行选择ID,其中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
Run Code Online (Sandbox Code Playgroud)

编辑

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

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

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

因为内部选择可能返回零结果,所以它将返回NULL值,这也是不可接受的.调查内部选择为什么在零和x之间返回结果,这个问题萌生了(这甚至是英语吗?).

回答

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

谢谢大家回答,慢慢但肯定会让我更好地理解.

Dan*_*haw 8

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

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

SELECT  * , NEWID()
FROM    @Table  
Run Code Online (Sandbox Code Playgroud)

这将修复您的代码:

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

SELECT  * 
FROM    @Table  
WHERE   ID = @Id
Run Code Online (Sandbox Code Playgroud)

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

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

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

编辑2:要在用作子查询时获得所需的结果,请使用TOP 1,如下所示:

SELECT  t.ID 
        , FK1 = (SELECT TOP 1 FK1 FROM @Table ORDER BY NEWID()) 
FROM    @Table t
Run Code Online (Sandbox Code Playgroud)