Случайный ряд из Линк в Sql - PullRequest
109 голосов
/ 15 марта 2009

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

Ответы [ 15 ]

166 голосов
/ 15 марта 2009

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

Другой пример для Entity Framework:

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

Это не работает с LINQ to SQL. OrderBy просто сбрасывается.

30 голосов
/ 15 марта 2009

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;
}
18 голосов
/ 27 июля 2010

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

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

7 голосов
/ 03 августа 2011

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

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

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

1 голос
/ 23 июня 2014
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 будет случайным.

1 голос
/ 11 апреля 2011

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

0 голосов
/ 08 октября 2017

Добавить к решению Марка Гравелла. Если вы не работаете с самим классом 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());
}
0 голосов
/ 26 июня 2016
var cust = (from c in ctx.CUSTOMERs.ToList() select c).OrderBy(x => x.Guid.NewGuid()).Taket(2);

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

0 голосов
/ 13 ноября 2015

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

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

[Function(Name = "NEWID", IsComposable = true)]
public Guid Random()
{
    throw new NotImplementedException();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...