2016-07-26 84 views
2

每次都必须在得到从表中10个随机行,但是当我重复查询行永远不会重复。从SQL Server获取随机数据,但没有重复值

但是,如果我得到所有的行,它会从一个重复,如表中有20行,第一次我得到10个随机行,第二次,我将需要得到剩余10行,并在我的第三个查询我需要随机获得10行。

目前我用于获取10行随机查询:

SELECT TOP 10 * 
FROM tablename 
ORDER BY NEWID() 

但MSDN建议此查询

SELECT TOp 10 * FROM Table1 
    WHERE (ABS(CAST(
    (BINARY_CHECKSUM(*) * 
    RAND()) as int)) % 100) < 10 

获得良好的性能。但是这个查询不会返回常量行。能否请您提出这个

+0

显示样本数据请。 – NEER

回答

2

东西因为你的第二个查询所需要的结果取决于第一个查询的(随机)的结果,该查询不能是无状态的。您需要以某种方式在某处存储状态(关于以前的查询/查询的信息)。

最简单的解决方案可能会被存储已经检索到的行或它们的ID在一个临时表,然后在第二个查询查询... where id not in (select id from temp_table)

+0

数据非常大,就像我们有20000行和更多,那么我们如何将数据存储到临时表中并一次又一次地运行,它会降低性能,对吗? – Manikandan

+1

是的,当你向临时表中添加记录时,性能会下降,但是如果你想得到结果,那么你唯一的选择就是将已经选择的行存储在某个地方,也许一个带索引的表可以为你提供足够的性能。 – Doliveras

+0

20k行不是太多的数据。例如。如果你的行有一个整数ID并且你只存储了使用行的ID,那可能意味着你的临时表可能适合100kB,这可以很容易地缓存在内存中,甚至可以在没有索引的情况下快速访问。 –

0

正如吉日Tousek说,每运行一个查询必须知道什么返回前面的查询。

不是将先前返回的行的ID插入表中,而是检查新的结果是不是在该表中,我只是简单地向表中添加一列,并使用随机数来定义一个新的随机顺序的行。

您需要用随机数一次此列。

这会记得行的随机顺序,并使其稳定,因此,所有你需要你的查询之间要记住的是多少随意行到现在为止您要求。然后,从您在先前查询中停止的位置开始,根据需要获取尽可能多的行。


将一列RandomNumber binary(8)添加到表中。您可以选择不同的尺寸。 8个字节应该足够了。

用随机数填充它。一旦。

UPDATE tablename 
SET RandomNumber = CRYPT_GEN_RANDOM(8) 

RandomNumber列创建索引。独特的索引。如果事实证明有重复的随机数(这对于20,000行是不可能的,对于8个字节的随机数是不可能的),然后重新生成随机数(再次运行UPDATE语句),直到所有数字都是唯一的。

申请前10个随机行:

SELECT TOP(10) * 
FROM tablename 
ORDER BY RandomNumber 

当你处理/使用这些10个随机行记得上次使用的随机数。最好的方法取决于你如何处理这10个随机行。

DECLARE @VarLastRandomNumber binary(8); 
SET @VarLastRandomNumber = ... 
-- the random number from the last row returned by the previous query 

请求下一个10个随机行:

SELECT TOP(10) * 
FROM tablename 
WHERE RandomNumber > @VarLastRandomNumber 
ORDER BY RandomNumber 

过程并记住上次使用的随机数。

重复。作为奖励,您可以在每次迭代中请求不同数量的随机行(每次不一定是10)。

+1

可行的方式,如果a)OP是否能够修改表本身,b)该表在整个过程中稳定新的或删除的行),c)一次只有一个会话执行此过程。 OP没有提供任何具体细节,所以我们不知道。 –

0

我会做的是有两个新的领域,SELECTED(int)和TimesSelected(整数),然后

UPDATE tablename SET SELECTED = 0; 

WITH CTE AS (SELECT TOP 10 * 
FROM tablename 
ORDER BY TimesSelected ASC, NEWID()) 
UPDATE CTE SET SELECTED = 1, TimesSelected = TimesSelected + 1; 

SELECT * from tablename WHERE SELECTED = 1; 

因此,如果您使用的每一次,一旦选择了记录转到桩顶,并且它下面的记录是随机选择的。

,你可能想要把所选择的指数,并做

UPDATE tablename SET SELECTED = 0 WHERE SELECTED = 1; -- for performance 
+0

是的,我认为这将工作,一旦记录被选中,成为1它不能重新选择,直到所有其他记录是1,当发生这种情况时,一些记录将成为2,然后不符合条件,直到其他人赶上 – Cato

0

最优雅的解决方案,只要你做了一定的时间内,连续查询,将使用游标:

DECLARE rnd_cursor CURSOR FOR 
    SELECT col1, col2, ... 
    FROM tablename 
    ORDER BY NEWID(); 

OPEN rnd_cursor; 
FETCH NEXT FROM rnd_cursor; -- Repeat ten times 

保持光标处于打开状态,只需在需要时随时获取行。关闭游标时,即可大功告成:

CLOSE rnd_cursor; 
DEALLOCATE rnd_cursor; 

至于你的问题的第二部分,一旦你获取了最后一排,打开一个新的光标:

IF @@FETCH_STATUS <> 0 
BEGIN 
    CLOSE rnd_cursor; 
    OPEN rnd_cursor; 
END;