从Linq到Sql的随机行

当我有条件时,使用Linq to SQL检索随机行的最好(也是最快)方法是什么,例如某些字段必须为真?

你可以通过使用一个假的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服务器提供一个随机的顺序。 即

 var cust = (from row in ctx.Customers where row.IsActive // your filter orderby ctx.Random() select row).FirstOrDefault(); 

请注意,这只适用于中小型的桌子; 对于巨大的表,它将在服务器上产生性能影响,find行数( Count )会更有效率,然后随机select一个( 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 

entity framework的另一个示例:

 var customers = db.Customers .Where(c => c.IsActive) .OrderBy(c => Guid.NewGuid()) .FirstOrDefault(); 

这不适用于LINQ to SQL。 OrderBy只是被丢弃。

编辑:我只是刚刚注意到这是LINQ到SQL,而不是LINQ到对象。 使用Marc的代码让数据库为你做这个。 我在这里留下了这个答案,作为LINQ to Objects的一个潜在兴趣点。

奇怪的是,你实际上并不需要计数。 但是,你需要去除每一个元素,除非你能清点。

你可以做的是保持“当前”值和当前计数的想法。 取下一个值时,取一个随机数,用“新”replace“当前”,概率为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; } 

一种有效实现的方法是在数据Shuffle数据中添加一列, Shuffle填入随机数(随着每条logging的创build)。

以随机顺序访问表的部分查询是…

 Random random = new Random(); int seed = random.Next(); result = result.OrderBy(s => (~(s.Shuffle & seed)) & (s.Shuffle | seed)); // ^ seed); 

这会在数据库中执行XOR操作,并按该XOR的结果进行sorting。

优点:-

  1. 高效:SQL处理sorting,不需要获取整个表
  2. 可重复性:(对testing有用) – 可以使用相同的随机种子生成相同的随机顺序

这是我的家庭自动化系统用来随机播放列表的方法。 它每天select一个新种子,在白天给出一个一致的顺序(允许简单的暂停/恢复function),而是在每个新的每一天播放列表上重新审视。

如果你想从表中得到例如var count = 16随机行,你可以写

 var rows = Table.OrderBy(t => Guid.NewGuid()) .Take(count); 

这里我使用EF,而表是Dbset

如果获得随机行的目的是取样,那么我在这里简单地谈了一下Larson等人的一个很好的方法,他们使用物化视图为Microsoft SQL Server开发了一个抽样框架。 还有一个链接到实际的纸张也。

来这里想知道如何从less数几个随机页面中获得,所以每个用户得到一些不同的随机3页。

这是我最后的解决scheme,用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 

在查询大量结果之前,可能应该进行一些分析,但这对于我的目的来说是完美的

 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的顺序是随机的。

我有对DataTable的随机函数查询:

 var result = (from result in dt.AsEnumerable() order by Guid.NewGuid() select result).Take(3); 

下面的例子将调用源来检索一个计数,然后应用一个0到n之间的数字来源的跳过expression式。 第二种方法是通过使用随机对象(将内存中的所有内容sorting)并select传入方法调用的编号来应用顺序。

 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); } } 

我用这个方法取随机新闻,工作很好;)

  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; } 

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

如果您使用LINQPad ,切换到C#编程模式,并执行此操作:

 void Main() { YourTable.OrderBy(v => Random()).FirstOrDefault.Dump(); } [Function(Name = "NEWID", IsComposable = true)] public Guid Random() { throw new NotImplementedException(); } 
 var cust = (from c in ctx.CUSTOMERs.ToList() select c).OrderBy(x => x.Guid.NewGuid()).Taket(2); 

select随机2行

添加到Marc Gravell的解决scheme。 如果你不使用datacontext类本身(因为你以某种方式代理它,例如为了testing目的伪造datacontext),你不能直接使用定义的UDF:它不会被编译为SQL,因为你没有在您的真实数据上下文类的子类或部分类。

解决这个问题的方法是在你的代理中创build一个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 datacontext(在内存实体中使用)中实现这一点:

 public IOrderedQueryable<T> Randomize<T>(IQueryable<T> query) { return query.OrderBy(x => Guid.NewGuid()); }