从Linq到Sql的随机行

Jul*_*lin 110 .net c# linq-to-sql

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

Mar*_*ell 167

您可以使用假UDF在数据库中执行此操作; 在部分类中,向数据上下文添加方法:

partial class MyDataContext {
     [Function(Name="NEWID", IsComposable=true)] 
     public Guid Random() 
     { // to prove not used by our C# code... 
         throw new NotImplementedException(); 
     }
}
Run Code Online (Sandbox Code Playgroud)

然后就是order by ctx.Random(); 这将在SQL-Server提供随机排序NEWID().即

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

请注意,这仅适用于中小型表; 对于大型表,它会对服务器产生性能影响,找到行数(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
Run Code Online (Sandbox Code Playgroud)

  • 我知道这是旧的,但如果你从一个大表中选择许多随机行,请看:http://msdn.microsoft.com/en-us/library/cc441928.aspx我不知道是否有LINQ当量. (8认同)
  • 如果是*过滤器后30k*,我会说不:不要使用这种方法.做2次往返; 1获取Count(),1获得随机行... (3认同)
  • @Neal S.:ctx.Random()的订单可以与Take(5)混合使用; 但如果您使用Count()方法,我预计6次往返是最简单的选择. (2认同)

Kon*_*kus 59

实体框架的另一个示例:

var customers = db.Customers
                  .Where(c => c.IsActive)
                  .OrderBy(c => Guid.NewGuid())
                  .FirstOrDefault();
Run Code Online (Sandbox Code Playgroud)

这不适用于LINQ to SQL.在OrderBy简单地被丢弃.

  • 这在LINQ to SQL中不起作用......也许它在Entity Framework 4中工作(不确认).如果要对List进行排序,则只能使用.OrderBy和Guid.使用DB它将无法正常工作. (8认同)
  • 您有没有对此进行分析并确认它有效?在我使用LINQPad的测试中,order by子句被删除. (4认同)
  • 只是为了最终确认这在EF4中有效 - 在这种情况下它是很好的选择. (2认同)

Jon*_*eet 31

编辑:我只是注意到这是LINQ to SQL,而不是LINQ to Objects.使用Marc的代码让数据库为您完成此操作.我在这里留下这个答案作为LINQ to Objects的潜在兴趣点.

奇怪的是,你实际上并不需要计算.但是,除非得到计数,否则你需要获取每个元素.

你能做的就是保持"当前"值和当前计数的概念.当你获取下一个值时,取一个随机数并用"new"替换"current",概率为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;
}
Run Code Online (Sandbox Code Playgroud)

  • 仅供参考 - 我进行了快速检查,这个函数确实具有统一的概率分布(递增计数与Fisher-Yates shuffle的机制基本相同,所以它应该是合理的). (4认同)

Ian*_*cer 19

实现高效实现的一种方法是在数据Shuffle中添加一个用随机int填充的列(当创建每个记录时).

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

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

这在数据库中执行XOR操作,并按XOR的结果进行排序.

好处:-

  1. 高效:SQL处理排序,无需获取整个表
  2. 可重复:(适合测试) - 可以使用相同的随机种子生成相同的随机顺序

这是我的家庭自动化系统用于随机化播放列表的方法.它每天都会选择一个新的种子,在白天提供一致的顺序(允许轻松的暂停/恢复功能),但每个新的一天都要重新审视每个播放列表.


Art*_*yan 7

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

var rows = Table.OrderBy(t => Guid.NewGuid())
                        .Take(count);
Run Code Online (Sandbox Code Playgroud)

这里我使用EF,表是Dbset