在SQL Server中选择N条随机记录,不重复

在SQL Server中选择N条随机记录,不重复,sql,sql-server,fluent-nhibernate,random,unique,Sql,Sql Server,Fluent Nhibernate,Random,Unique,如何一次从表中选择N条随机记录,而不重复以前由同一操作返回的记录 一个显而易见的解决办法是: SELECT TOP 5 * FROM MyTable WHERE Id NOT IN (SELECT Id FROM PreviouslyReturned) ORDER BY newid() 但随着MyTable开始增长,这不是真的很低效吗 我有一个很长的记录列表,我要求一个回合的游戏每次有五个记录,而不重复给定游戏中已经提取的任何记录。因为我知道大概会发生多少回合,所以我可以在游戏开始前随机选择一

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

一个显而易见的解决办法是:

SELECT TOP 5 * FROM
MyTable
WHERE Id NOT IN (SELECT Id FROM PreviouslyReturned)
ORDER BY newid()
但随着MyTable开始增长,这不是真的很低效吗

我有一个很长的记录列表,我要求一个回合的游戏每次有五个记录,而不重复给定游戏中已经提取的任何记录。因为我知道大概会发生多少回合,所以我可以在游戏开始前随机选择一个很大的样本,但我宁愿它是动态的。我发现,它使用MySQL的随机种子


最终会有如此多的记录,重复不会成为记录>>N的问题,但在此之前,我需要记录是唯一的。另一方面,我使用Fluent NHibernate作为持久层;也许NHibernate有一些特性允许这样做

没有重复任何记录

这不是在程序员中随机选择记录。随机选择的值重复的次数比你想象的要多,事实上统计重复的次数和长度是统计学家检测作弊的一种方法

你要找的就是洗牌。洗牌使有限事物的顺序随机化,如卡片或钥匙。随机化行的顺序与选择随机行并不意味着相同的事情

在您的情况下,计划存储已用于每个用户的密钥集。随机选择一组不在该集中的行。有几种方法可以存储每一组随机的行键;确保你能分辨出哪一个是最后一组还是当前一组。

试试看

SELECT TOP 5 *
FROM YOUR_TABLE
ORDER BY CHECKSUM(NEWID())
这里有一个相关的问题详细介绍了随机洗牌选择的语义:

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()

您可以将表的所有主键存储在第二个表中,从这个表中随机选择,从原始表中检索关联的行,当然,从辅助表中选择后删除它们

我希望这种方法比存储已经使用的密钥和构建WHERE-NOR-IN-resp更有效。除此之外,删除子句的性能应与插入子句的性能大致相同,选择子句的速度应大大加快,而无需附加子句。但这当然要通过分析来证明


使用您所引用的方法,选择五个随机ID应该可以很好地工作。

最终将有如此多的记录,重复不会成为问题。我不知道你怎么能做出这样的陈述,除非你能保证你的伪随机数生成器不会连续两次给你相同的值。@吉姆,我的意思是,一个月重复一次对我的应用程序来说没什么大不了的。你的直觉可能是错的。是的,你是对的。这是一场洗牌。如果我有几千行,并且我已经提取了100行,我会跟踪这些行,那么每个后续的选择都需要一个WHERE NOT IN select id FROM Used_rows子句,这看起来非常低效。或者是吗?有不止一种方法可以表示在所用行的SELECT id中不存在的位置。SQL Server 2005+支持,但不支持,例如。不同的表达式可能会给出不同的执行计划。如果只有几千行存在真正的性能问题,那么首先可能是选择随机行,而不是按所用行中的值进行过滤。为什么要在校验和中包装NEWID。。。是否影响行的无序排列,而不是更改顺序?同一输入的会话或查询之间的校验和输出是否不同?@FreshCode:不会。在这种情况下,校验和是无关紧要的。事实上,这是一个偶然的结果。纽伊德的命令将独自完成这项任务。MS SQL对每个查询计算一次RAND,对每行计算一次NEWID,这是重要的一点。关于不同会话之间的校验和变化,您可能想单独问这个问题。@Chris,我知道每个采样都是独立的,可能会返回以前采样返回的行。我编辑了我的问题以澄清这一点。@Chris,谢谢。WHERE pr.Id为NULL的左连接不等于WHERE NOT IN@FreshCode:不总是这样。当使用空值时,请注意不在行为中的位置。i、 e.如果Id不在1,2,NULL等中,我会以任何方式比较执行计划。。。我想我可以有一个全局洗牌表,当它为空时会被补充,但是如果原始数据发生变化,插入新记录将是一件烦琐的事情。