#1 楼
您可以使用伪造的UDF在数据库中执行此操作;在部分类中,向数据上下文添加一个方法:partial class MyDataContext {
[Function(Name="NEWID", IsComposable=true)]
public Guid Random()
{ // to prove not used by our C# code...
throw new NotImplementedException();
}
}
然后仅
order by ctx.Random()
;这将根据NEWID()
在SQL Server上进行随机排序。即var cust = (from row in ctx.Customers
where row.IsActive // your filter
orderby ctx.Random()
select row).FirstOrDefault();
请注意,这仅适用于中小型桌子;对于巨大的表,这将对服务器的性能产生影响,找到行数(
Count
),然后随机选择一个行(Skip/First
),效率会更高。用于计数方法:
var qry = from row in ctx.Customers
where row.IsActive
select row;
int count = qry.Count(); // 1st round-trip
int index = new Random().Next(count);
Customer cust = qry.Skip(index).FirstOrDefault(); // 2nd round-trip
评论
如果在过滤器之后30k,我会说不:不要使用这种方法。进行两次往返; 1获得Count(),1获得随机行...
– Marc Gravell♦
09年3月15日在19:59
如果要五行(或“ x”)随机行怎么办?是最好只进行六次往返,还是有便捷的方法在存储过程中实现它?
–尼尔·斯图伦
09年7月2日14:00
@Neal S .:可以将ctx.Random()的顺序与Take(5)混合;但是,如果您使用Count()方法,则我希望6次往返是最简单的选择。
– Marc Gravell♦
09年7月2日在14:46
不要忘记添加对System.Data.Linq的引用,否则System.Data.Linq.Mapping.Function属性将无法正常工作。
– Jaguir
2010-2-19在22:10
我知道这很旧,但是如果您从一个大表中选择许多随机行,请参见:msdn.microsoft.com/zh-cn/library/cc441928.aspx我不知道是否有LINQ等效项。
– jwd
2011年9月8日在22:39
#2 楼
实体框架的另一个示例:var customers = db.Customers
.Where(c => c.IsActive)
.OrderBy(c => Guid.NewGuid())
.FirstOrDefault();
这不适用于LINQ to SQL。
OrderBy
只是被丢弃。评论
您是否对此资料进行了分析并确认它有效?在我使用LINQPad的测试中,order by子句被删除。
– Jim Wooley
2011-3-24 14:56
这是解决此问题的最佳方法
–reach4thelasers
2012年5月24日14:25
这在LINQ to SQL中不起作用...也许在Entity Framework 4中起作用(未确认)。如果您要对DB排序List ...,则只能将.OrderBy与Guid一起使用,它将无法工作。
– nikib3ro
2012年5月30日在21:56
只是为了最终确认它可以在EF4中工作-在这种情况下,这是一个不错的选择。
– nikib3ro
13年3月12日在5:04
您能否编辑答案并解释为什么带新Guid的orderBy可以解决问题?顺便说一句好答案:)
–让-弗朗索瓦·科特(Jean-FrançoisCôté)
2013年6月7日12:15
#3 楼
编辑:我只注意到这是LINQ to SQL,而不是LINQ to Objects。使用Marc的代码获取数据库即可为您完成此任务。我把这个答案留在这里,作为LINQ to Objects的潜在兴趣点。奇怪的是,您实际上并不需要计数。但是,您确实需要获取每个元素,除非获得计数。
您可以做的是保持“当前”值和当前计数的想法。当您获取下一个值时,请随机取一个数字,并以1 / n的概率将“当前”替换为“新”,其中n是计数。
因此,当您读取第一个值时,您总是将其设为“当前”值。当您读取第二个值时,您可能会将其设为当前值(概率1/2)。当您读取第三个值时,您可能会得出当前值(概率1/3)等。当数据用完时,当前值是所有读取的值中的随机值,并且概率均匀。
要应用该条件,只要忽略不符合条件的内容即可。最简单的方法是只考虑“匹配”序列,首先应用Where子句。
这是一个快速实现。我觉得还可以...
public static T RandomElement<T>(this IEnumerable<T> source,
Random rng)
{
T current = default(T);
int count = 0;
foreach (T element in source)
{
count++;
if (rng.Next(count) == 0)
{
current = element;
}
}
if (count == 0)
{
throw new InvalidOperationException("Sequence was empty");
}
return current;
}
评论
仅供参考-我进行了快速检查,此函数的确具有均匀的概率分布(递增计数与Fisher-Yates随机播放本质上是相同的机制,因此似乎应该是合理的)。
–格雷格·比奇(Greg Beech)
09年3月15日在18:54
@Greg:太好了,谢谢。快速检查对我来说似乎还可以,但是在这样的代码中一次又一次地出错很容易。当然,这实际上与LINQ与SQL无关,但是仍然很有用。
–乔恩·斯基特(Jon Skeet)
09年3月15日在18:57
@JonSkeet,您好,您能检查一下并让我知道我在想什么吗
– Shaiju T
2015年6月10日9:54
@TylerLaing:不,这并不意味着休息。在第一次迭代中,电流将始终设置为第一个元素。在第二次迭代中,将其设置为第二个元素有50%的变化。在第三次迭代中,有33%的机会将其设置为第三个元素。添加break语句意味着您将始终在读取第一个元素后退出,从而使其根本不是随机的。
–乔恩·斯基特(Jon Skeet)
18-2-28在7:48
@JonSkeet Doh!我误读了您对count的使用(例如,本来以为这是Fisher-Yates风格,具有n-i之类的随机范围)。但是要选择Fisher-Yates中的第一个元素,就是要公平地选择任何元素。但是,这需要知道元素的总数。现在,我知道您的解决方案对于IEnumerable而言是整洁的,因为总计数是未知的,并且不需要遍历整个源仅获取计数,然后再次遍历一些随机选择的索引。就像您所说的那样,它可以一口气解决:“除非获得计数,否则需要获取每个元素”。
–泰勒·莱恩
18年2月28日在20:40
#4 楼
一种有效实现方法是在数据Shuffle
中添加一列,该列填充有一个随机的int(创建每个记录时)。 以随机顺序访问表的部分查询是...
Random random = new Random();
int seed = random.Next();
result = result.OrderBy(s => (~(s.Shuffle & seed)) & (s.Shuffle | seed)); // ^ seed);
这在数据库中执行XOR操作,并按该XOR的结果。
优点:-
高效:SQL处理
排序,无需获取全部
表
可重复:(适用于
测试)-可以使用相同的随机数
种子生成相同的随机数
订单
这是使用的方法由我的家庭自动化系统随机分配播放列表。它每天都会选择一个新种子,在一天中提供一致的顺序(允许轻松的暂停/恢复功能),但每天新刷新每个播放列表。
评论
如果您不使用随机int字段而只使用现有的自动递增身份字段(种子显然会保持随机),则会对随机性产生什么影响?还有-最大值等于表中记录数的种子值是否足够或应该更高?
–布莱恩(Bryan)
2011年10月13日在17:19
同意,这是一个很好的答案,国际海事组织应该对此做出更多投票。我在实体框架查询中使用了此方法,按位XOR运算符^似乎可以直接工作,从而使条件变得更简洁:result = result.OrderBy(s => s.Shuffle ^ seed); (即无需通过〜,&和|运算符实现XOR)。
–史蒂文·兰德
16年6月29日在10:21
#5 楼
如果你想得到例如var count = 16
表中的随机行,可以写var rows = Table.OrderBy(t => Guid.NewGuid())
.Take(count);
这里我用的是E.F,而表是Dbset
#6 楼
如果获取随机行的目的是采样,我在这里已经简短地谈到了Microsoft Research团队Larson等人的一种不错的方法,他们在其中使用实例化视图为Sql Server开发了一个采样框架。也有指向实际论文的链接。#7 楼
List<string> lst = new List<string>();
lst.Add("Apple");
lst.Add("Guva");
lst.Add("Graps");
lst.Add("PineApple");
lst.Add("Orange");
lst.Add("Mango");
var customers = lst.OrderBy(c => Guid.NewGuid()).FirstOrDefault();
说明:通过插入GUID(是随机的),具有orderby的订单将是随机的。
评论
指导不是“随机的”,而是非顺序的。它们是有区别的。实际上,对于这种琐碎的事情来说,这可能并不重要。
–克里斯·马里西奇(Chris Marisic)
2014年9月17日17:23
#8 楼
来到这里想知道如何从少数几个随机页面中获取一些随机页面,因此每个用户都会得到一些不同的随机3页。这是我的最终解决方案,使用LINQ对Sharepoint 2010中的页面列表进行查询。在Visual Basic中,对不起:p
Dim Aleatorio As New Random()
Dim Paginas = From a As SPListItem In Sitio.RootWeb.Lists("Páginas") Order By Aleatorio.Next Take 3
大概应该在查询大量结果之前进行一些分析,但这对我来说是完美的
#9 楼
我对DataTable
s有随机函数查询:var result = (from result in dt.AsEnumerable()
order by Guid.NewGuid()
select result).Take(3);
#10 楼
下面的示例将调用源以检索计数,然后在源上应用一个介于0和n之间的数字的跳过表达式。第二种方法将通过使用随机对象(将对内存中的所有内容进行排序)来应用顺序,并选择传递给方法调用的数字。public static class IEnumerable
{
static Random rng = new Random((int)DateTime.Now.Ticks);
public static T RandomElement<T>(this IEnumerable<T> source)
{
T current = default(T);
int c = source.Count();
int r = rng.Next(c);
current = source.Skip(r).First();
return current;
}
public static IEnumerable<T> RandomElements<T>(this IEnumerable<T> source, int number)
{
return source.OrderBy(r => rng.Next()).Take(number);
}
}
评论
一些解释会很好
–安德鲁·巴伯(Andrew Barber)
2012年10月9日10:00
此代码不是线程安全的,只能在单线程代码中使用(因此不能用于ASP.NET)
–克里斯·马里西奇(Chris Marisic)
2014年9月17日下午17:24
#11 楼
我使用这种方法来获取随机新闻,并且效果很好;) public string LoadRandomNews(int maxNews)
{
string temp = "";
using (var db = new DataClassesDataContext())
{
var newsCount = (from p in db.Tbl_DynamicContents
where p.TimeFoPublish.Value.Date <= DateTime.Now
select p).Count();
int i;
if (newsCount < maxNews)
i = newsCount;
else i = maxNews;
var r = new Random();
var lastNumber = new List<int>();
for (; i > 0; i--)
{
int currentNumber = r.Next(0, newsCount);
if (!lastNumber.Contains(currentNumber))
{ lastNumber.Add(currentNumber); }
else
{
while (true)
{
currentNumber = r.Next(0, newsCount);
if (!lastNumber.Contains(currentNumber))
{
lastNumber.Add(currentNumber);
break;
}
}
}
if (currentNumber == newsCount)
currentNumber--;
var news = (from p in db.Tbl_DynamicContents
orderby p.ID descending
where p.TimeFoPublish.Value.Date <= DateTime.Now
select p).Skip(currentNumber).Take(1).Single();
temp +=
string.Format("<div class=\"divRandomNews\"><img src=\"files/1364193007_news.png\" class=\"randomNewsImg\" />" +
"<a class=\"randomNews\" href=\"News.aspx?id={0}\" target=\"_blank\">{1}</a></div>",
news.ID, news.Title);
}
}
return temp;
}
#12 楼
在LINQPad中使用LINQ to SQL作为C#语句看起来像IEnumerable<Customer> customers = this.ExecuteQuery<Customer>(@"SELECT top 10 * from [Customers] order by newid()");
customers.Dump();
生成的SQL是
SELECT top 10 * from [Customers] order by newid()
#13 楼
如果使用LINQPad,请切换到C#程序模式并执行以下操作:void Main()
{
YourTable.OrderBy(v => Random()).FirstOrDefault.Dump();
}
[Function(Name = "NEWID", IsComposable = true)]
public Guid Random()
{
throw new NotImplementedException();
}
#14 楼
var cust = (from c in ctx.CUSTOMERs.ToList() select c).OrderBy(x => x.Guid.NewGuid()).Taket(2);
随机选择2行
#15 楼
添加到Marc Gravell的解决方案中。如果您不使用datacontext类本身(因为您以某种方式代理它,例如为了测试目的而伪造datacontext),则不能直接使用定义的UDF:因为您没有在数据库中使用它,所以它不会被编译为SQL。实际数据上下文类的子类或局部类。解决此问题的一种方法是在代理中创建一个Randomize函数,并将其提供给您想要随机化的查询:
public class DataContextProxy : IDataContext
{
private readonly DataContext _context;
public DataContextProxy(DataContext context)
{
_context = context;
}
// Snipped irrelevant code
public IOrderedQueryable<T> Randomize<T>(IQueryable<T> query)
{
return query.OrderBy(x => _context.Random());
}
}
在您的代码中使用它的方式如下:
var query = _dc.Repository<SomeEntity>();
query = _dc.Randomize(query);
为了完整起见,这是在FAKE数据上下文(在内存实体中使用)的实现方式):
public IOrderedQueryable<T> Randomize<T>(IQueryable<T> query)
{
return query.OrderBy(x => Guid.NewGuid());
}
评论
对于检查真实条件的订单,您有两个选择。如果大多数物品上都出现了真实情况,则只需随机抓取一个物品,然后测试并在错误时重复。如果很少,请让数据库将选项限制为真实条件,然后随机获取一个。与该站点上的许多答案一样-二级评价比公认的要好得多。