代码之家  ›  专栏  ›  技术社区  ›  Julien Poulin

从LINQ到SQL的随机行

  •  109
  • Julien Poulin  · 技术社区  · 16 年前

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

    15 回复  |  直到 7 年前
        1
  •  165
  •   Armstrongest    14 年前

    您可以在数据库中使用一个伪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
    
        2
  •  58
  •   usr    10 年前

    实体框架的另一个示例:

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

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

        3
  •  29
  •   Jon Skeet    16 年前

    编辑:我只注意到这是Linq to SQL,而不是Linq to对象。使用Marc的代码让数据库为您完成这项工作。我把这个答案留在这里,作为LinqToObjects的潜在兴趣点。

    奇怪的是,你实际上不需要计算。但是,您需要获取每个元素,除非您获得计数。

    你能做的就是保持“当前”值和当前计数的概念。当您获取下一个值时,取一个随机数,将“current”替换为“new”,概率为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;
    }
    
        4
  •  18
  •   Ian Mercer    15 年前

    一种有效的方法是在数据中添加一列 Shuffle 它由一个随机整数填充(当创建每个记录时)。

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

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

    这将在数据库中执行XOR操作,并根据该XOR的结果进行排序。

    优点:

    1. 高效:SQL处理 订购,不需要取整个 桌子
    2. 可重复:(有利于 测试)-可以使用相同的随机 种子生成相同的随机数 秩序

    这是我家自动化系统用来随机化播放列表的方法。它每天选择一个新的种子,在一天中给出一个一致的顺序(允许轻松的暂停/恢复功能),但在每个新的一天重新查看每个播放列表。

        5
  •  7
  •   Artur Keyan    14 年前

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

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

    这里我用了e.f,表是一个dbset

        6
  •  1
  •   naiemk    14 年前

    如果获取随机行的目的是抽样,我已经简单地谈过了 here 关于Larson等人的一个好方法,微软研究团队已经开发了一个使用物化视图的SQL Server采样框架。还有一个链接指向实际的纸张。

        7
  •  1
  •   Fran    13 年前

    来这里想知道如何从一小部分随机页面中获取几个随机页面,这样每个用户都会得到一些不同的随机3个页面。

    这是我的最终解决方案,使用Linq查询SharePoint2010中的页面列表。它在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
    

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

        8
  •  1
  •   Boas Enkler    11 年前
    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(随机),order by的顺序将是随机的。

        9
  •  0
  •   user7116    13 年前

    我有随机函数查询 DataTable S:

    var result = (from result in dt.AsEnumerable()
                  order by Guid.NewGuid()
                  select result).Take(3); 
    
        10
  •  0
  •   dmportella Balazs Tihanyi    12 年前

    下面的示例将调用源以检索计数,然后对数字介于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);
        }
    }
    
        11
  •  0
  •   sadati    12 年前

    我用这种方法来获取随机新闻,它的工作很好;)

        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
  •  0
  •   iCollect.it Ltd    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
  •  0
  •   alexey    9 年前

    如果你使用 林帕德 切换到 C语言程序 按以下方式操作:

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

    随机选择2行

        15
  •  0
  •   Dave de Jong    7 年前

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

    解决此问题的方法是在代理中创建一个随机化函数,向其提供要随机化的查询:

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

    要完成,这是如何在假数据上下文(使用内存中的实体)中实现此功能:

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