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

时间:2010-06-14 08:16:07

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

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

由于我们的输入表恰好包含5行且具有唯一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

修改

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

最终,我们必须使用逼真值创建结果,其中生产者和权重的组合模糊,方法是从表格中随机选择现有权重。
那么查询就会变成这样的东西(这也是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)会被评估一次,然后匹配。

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

6 个答案:

答案 0 :(得分:8)

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

要查看此内容,请运行以下查询。您会看到每一行都有不同的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保证是连续的

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

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

答案 1 :(得分: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

答案 2 :(得分:1)

请尝试以下操作,以便了解会发生什么:

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

答案 3 :(得分:1)

或者您可以使用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 

答案 4 :(得分:1)

这可以帮助您了解原因。 多次运行查询。 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

答案 5 :(得分:0)

我不知道这会对你有多大帮助,但试试这个..我明白你每次执行查询时都想要一个随机行..

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

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