从Linq到Sql的随机行

时间:2009-03-15 17:37:16

标签: c# .net linq-to-sql

当我遇到条件时,使用Linq to SQL检索随机行的最佳(也是最快)方法是什么?某些领域必须是真的吗?

15 个答案:

答案 0 :(得分:166)

您可以使用假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

答案 1 :(得分:58)

实体框架的另一个示例:

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

这不适用于LINQ to SQL。 OrderBy只是被删除了。

答案 2 :(得分:29)

编辑:我刚才注意到这是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;
}

答案 3 :(得分:18)

实现高效实现的一种方法是在数据Shuffle中添加一列,该列填充了随机int(创建每条记录时)。

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

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

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

优点: -

  1. 高效:SQL处理 订购,无需取整 表
  2. 可重复:(适合 测试) - 可以使用相同的随机 种子生成相同的随机 为了
  3. 这是我的家庭自动化系统用于随机化播放列表的方法。它每天都会选择一个新的种子,在白天提供一致的顺序(允许轻松的暂停/恢复功能),但每个新的一天都会重新查看每个播放列表。

答案 4 :(得分:7)

如果你想得到例如来自表的var count = 16个随机行,你可以写

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

这里我使用了E.F,而表格是Dbset

答案 5 :(得分:1)

如果获取随机行的目的是抽样,我已经非常简短地谈到了{Larson等人,Microsoft研究团队的一个很好的方法,他们使用物化视图为Sql Server开发了一个抽样框架。还有实际论文的链接。

答案 6 :(得分:1)

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

答案 7 :(得分:0)

我对DataTable s进行随机函数查询:

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

答案 8 :(得分:0)

来到这里想知道如何从少数几个中获取一些随机页面,因此每个用户获得一些不同的随机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 :(得分:0)

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

答案 10 :(得分:0)

下面的示例将调用源以检索计数,然后在源上应用跳过表达式,其数字介于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)

我使用此方法获取随机新闻并且其工作正常;)

    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)

如果您使用 LINQPad ,请切换到 C#program 模式并执行以下操作:

void Main()
{
    YourTable.OrderBy(v => Random()).FirstOrDefault.Dump();
}

[Function(Name = "NEWID", IsComposable = true)]
public Guid Random()
{
    throw new NotImplementedException();
}

答案 13 :(得分:0)

var cust = (from c in ctx.CUSTOMERs.ToList() select c).OrderBy(x => x.Guid.NewGuid()).Taket(2);

选择随机2行

答案 14 :(得分:0)

添加到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 datacontext(在内存实体中使用)中实现它:

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