Случайная строка из Linq в Sql


каков лучший (и самый быстрый) способ получить случайную строку с помощью Linq to SQL, когда у меня есть условие, например, какое-то поле должно быть истинным?

15 109

15 ответов:

вы можете сделать это в базе данных, используя поддельный 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-сервере любезно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

еще один пример для Entity Framework:

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

это не работает с LINQ to SQL. Элемент OrderBy просто упала.

EDIT: я только что заметил, что это LINQ to SQL, а не LINQ to Objects. Используйте код Марка, чтобы получить базу данных, чтобы сделать это за вас. Я оставил этот ответ здесь в качестве потенциальной точки интереса для 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;
}

один из способов добиться эффективности-это добавить столбец к вашим данным 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. Repeatable: (хорошо для тестирование) - можно использовать тот же случайный семя для генерации случайных порядок

это подход, используемый моей системой домашней автоматизации для рандомизации плейлистов. Он выбирает новое семя каждый день, давая последовательный порядок в течение дня (что позволяет легко приостанавливать / возобновлять возможности), но свежий взгляд на каждый плейлист каждый новый день.

Если вы хотите сделать например var count = 16 случайные строки из таблицы, вы можете написать

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

здесь я использовал E. F, и таблица является Dbset

Если целью получения случайных строк является выборка, я говорил очень кратко здесь о хорошем подходе от Ларсона и др., Исследовательская группа Microsoft, где они разработали структуру выборки для Sql Server с использованием материализованных представлений. Есть ссылка на реальный документ.

пришел сюда интересно, как получить несколько случайных страниц из небольшого количества из них, так что каждый пользователь получает некоторые различные случайные 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

вероятно, следует получить некоторое профилирование перед запросом большого количества результатов, но это идеально подходит для моей цели

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 будет случайным.

у меня есть случайный запрос-функция против DataTables:

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

в приведенном ниже примере будет вызван источник, чтобы получить счетчик, а затем применить выражение пропуска к источнику с числом от 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);
    }
}

Я использую этот метод для взятия случайных новостей и его работы отлично ;)

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

использование LINQ to SQL в LINQPad в качестве операторов 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()

Если вы используете LINQPad переключитесь на программы C# режим и сделать так:

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

[Function(Name = "NEWID", IsComposable = true)]
public Guid Random()
{
    throw new NotImplementedException();
}
var cust = (from c in ctx.CUSTOMERs.ToList() select c).OrderBy(x => x.Guid.NewGuid()).Taket(2);

выберите случайный 2 строки

чтобы добавить к решению Марка Гравелла. Если вы не работаете с самим классом 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);

чтобы быть полным, вот как реализовать это в поддельном datacontext (который использует в объектах памяти):

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