从SQL Server获取随机数据但没有重复值

时间:2016-07-26 11:07:50

标签: sql sql-server random shuffle random-data

我每次需要从表中获取10个随机行,但是当我重复查询时,行不会重复。

但是,如果我得到所有行,它将从一个重复,如表有20行,第一次我得到10个随机行,第二次我将需要剩余10行,在我的第3个查询我需要得到随机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 

表现良好。但是这个查询不返回常量行。你能否就此提出一些建议

4 个答案:

答案 0 :(得分:2)

由于第二个查询的必需结果取决于第一个查询的(随机)结果,因此查询不能是无状态查询。您需要以某种方式将状态(有关以前的查询/查询的信息)存储在某处。

最简单的解决方案可能是将已检索的行或其ID存储在临时表中,然后在第二个查询中查询... where id not in (select id from temp_table)

答案 1 :(得分:0)

正如Jiri 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)。

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

所以如果你每次都使用它,一旦选中了一个记录就到了堆的顶部,并且它下面的记录是随机选择的。

你可能想在SELECTED上放一个索引并执行

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

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