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

评论

对于检查真实条件的订单,您有两个选择。如果大多数物品上都出现了真实情况,则只需随机抓取一个物品,然后测试并在错误时重复。如果很少,请让数据库将选项限制为真实条件,然后随机获取一个。

与该站点上的许多答案一样-二级评价比公认的要好得多。

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