当我有条件时,使用Linq to SQL检索随机行的最佳(和最快)方法是什么,例如某些字段必须为true?
您可以使用假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()
; 这将在SQL-Server提供随机排序NEWID()
.即
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
实体框架的另一个示例:
var customers = db.Customers .Where(c => c.IsActive) .OrderBy(c => Guid.NewGuid()) .FirstOrDefault();
这不适用于LINQ to SQL.在OrderBy
简单地被丢弃.
编辑:我只是注意到这是LINQ to SQL,而不是LINQ to Objects.使用Marc的代码让数据库为您完成此操作.我在这里留下这个答案作为LINQ to Objects的潜在兴趣点.
奇怪的是,你实际上并不需要计算.但是,除非得到计数,否则你需要获取每个元素.
你能做的就是保持"当前"值和当前计数的概念.当你获取下一个值时,取一个随机数并用"new"替换"current",概率为1/n,其中n是计数.
因此,当您读取第一个值时,您始终将其设为"当前"值.当您读取第二个值时,您可能会将其设为当前值(概率1/2).当你读取第三个值时,你可能会得到当前值(概率1/3)等等.当你的数据用完时,当前值是你读过的所有值中的随机值,具有统一的概率.
要在条件中应用它,只需忽略任何不符合条件的东西.最简单的方法是首先考虑"匹配"序列,首先应用Where子句.
这是一个快速实现.我觉得没关系......
public static T RandomElement(this IEnumerable 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
中添加一个用随机int填充的列(当创建每个记录时).
以随机顺序访问表的部分查询是......
Random random = new Random(); int seed = random.Next(); result = result.OrderBy(s => (~(s.Shuffle & seed)) & (s.Shuffle | seed)); // ^ seed);
这在数据库中执行XOR操作,并按XOR的结果进行排序.
好处:-
高效:SQL处理排序,无需获取整个表
可重复:(适合测试) - 可以使用相同的随机种子生成相同的随机顺序
这是我的家庭自动化系统用于随机化播放列表的方法.它每天都会选择一个新的种子,在白天提供一致的顺序(允许轻松的暂停/恢复功能),但每个新的一天都要重新审视每个播放列表.
如果你想var count = 16
从表中获得例如随机行,你可以写
var rows = Table.OrderBy(t => Guid.NewGuid()) .Take(count);
这里我使用EF,表是Dbset