2011-04-24 149 views
1

如何一次从表中选择N个随机记录,而不重复之前由同一操作返回的记录?在SQL Server中选择N条随机记录而不重复

一个显而易见的解决方案是:

SELECT TOP 5 * FROM 
MyTable 
WHERE Id NOT IN (SELECT Id FROM PreviouslyReturned) 
ORDER BY newid() 

但是那不是因为MyTable的开始成长真的效率低下?

我有一长串记录,而且我一次需要5个记录来进行回合制游戏,而不需要重复任何已经为给定游戏拉动的记录。由于我大概知道会发生多少回合,所以我可以在比赛开始之前选择一个随机样本,这个样本非常大,但我宁愿它是“动态的”。我发现this question,它使用MySQL的随机种子。

最终会有这么多的记录,重复不会成为一个问题(记录>> N),但在那之前,我需要记录是唯一的。在旁注中,我为我的持久层使用了Fluent NHibernate;也许NHibernate有一些允许这个功能。

+1

“终究会有那么多的记录中的重复不会成为一个问题。”我不知道你怎么能做出这样的表述,除非你能保证你的伪随机数发生器不会连续给你两次相同的值。 – 2011-04-24 20:35:21

+0

@Jim,我的意思是说,在蓝色月亮中重复一次对我的应用来说并不是什么大事。 – 2011-04-25 07:41:31

+1

你的直觉在这里可能是错误的,[生日问题](http://en.wikipedia.org/wiki/Birthday_problem)。 – Ishtar 2011-04-28 11:23:28

回答

2

没有任何重复的记录

那不叫选择随机记录程序员之间。选择的数值随机重复的次数比您想象的要多,实际上统计重复的次数和长度是统计学家检测作弊的一种方式。

你在找什么叫做shuffle。混洗随机化事物的有限集合的顺序,如卡片或钥匙。 (随机化行的顺序并不意味着与选择随机行相同的事情。)

对于您的情况,计划存储已为每个用户使用的密钥集。选择一组不在该集合中的随机行。有几种方法可以存储每个随机的行(键)集合;确保你可以知道哪一个是最后的目前的集。

+0

是的,你是对的。这是一个洗牌。如果我有几千行并且我已经拉了100(我会跟踪),那么每个后续选择都需要一个'WHERE NOT IN(SELECT id FROM Used_Rows)'子句,这看起来效率很低。或者是? – 2011-04-25 07:45:19

+0

有多种方法可以表达'WHERE NOT IN(SELECT id FROM Used_Rows)'。例如,SQL Server 2005+支持'EXCEPT'。不同的表达可能会给你不同的执行计划。如果只有几千行存在真正的性能问题,则可能首先选择随机行,而不是按照Used_Rows中的值进行过滤。 – 2011-04-25 10:31:08

0

尝试

SELECT TOP 5 * 
FROM YOUR_TABLE 
ORDER BY CHECKSUM(NEWID()) 

有一个相关的问题是进入细节上随机洗牌选择这里的语义: Random select is not always returning a single row

SQL Server将评估RAND每查询一次,这意味着MySQL的诀窍无论如何不工作。

编辑:这也是充分

SELECT TOP 5 * 
FROM YOUR_TABLE 
ORDER BY NEWID() 

我看了你的更新问题,还有一个建议:
创建于

SELECT mt.* 
FROM MyTable mt 
LEFT JOIN PreviouslyReturned pr ON mt.Id = pr.Id 
WHERE pr.Id Is NULL 

或类似的索引视图

然后

SELECT TOP 5 * 
FROM YOUR_INDEXED_VIEW 
ORDER BY NEWID() 
+0

为什么在'CHECKSUM(...)'中包装'NEWID()'会影响除改变顺序之外的行的混洗? CHECKSUM的输出在会话或查询中对于相同的输入会有所不同吗? – 2011-04-25 07:59:00

+0

@FreshCode:它不会。在这种情况下,CHECKSUM无关紧要。它实际上是把它变成了意外答案。 ORDER BY NEWID()将独自完成。 MS SQL评估RAND()每查询一次,NEWID()每行一次,这是重要的一点。对于不同会议之间的CHECKSUM会有所不同,您可能想将其作为一个单独的问题提出。 – 2011-04-28 10:57:07

+0

@Chris,我知道每个采样都是独立的,可能会返回先前采样返回的行。我编辑了我的问题来澄清这一点。 – 2011-04-28 11:14:18

0

你可以只存储您的表的所有主键在第二个表,从这个(从原始表中检索相关行)随机选择和选择后删除它们(从辅助表,当然)。我希望这种方法比存储已经使用的键和构造一个WHERE NOR IN子句(或EXCEPT)子句更高效(删除应该与插入时大致一样,并且选择应该在没有附加子句的情况下显着更快地工作) 。但当然,这将不得不(通过剖析...)

选择五个随机ID应该工作正常使用的question你提到的方法来证明。

+0

我想我可以有一个全球性的“洗牌”表,它在空的时候被补充,但如果原来的数据改变会插入新的记录会是一件杂事。 – 2011-04-28 11:17:53