Який найкращий (і найшвидший) спосіб отримати випадковий рядок за допомогою Linq до SQL, коли у мене є умова, наприклад, якесь поле має бути правдивим?
Який найкращий (і найшвидший) спосіб отримати випадковий рядок за допомогою Linq до SQL, коли у мене є умова, наприклад, якесь поле має бути правдивим?
Відповіді:
Це можна зробити в базі даних, використовуючи підроблений 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
Ще один зразок для Entity Framework:
var customers = db.Customers
.Where(c => c.IsActive)
.OrderBy(c => Guid.NewGuid())
.FirstOrDefault();
Це не працює з LINQ в SQL. OrderBy
Просто при падінні.
EDIT: Я тільки що помітив, що це LINQ для SQL, а не LINQ для об'єктів. Використовуйте код Марка, щоб отримати базу даних, щоб зробити це за вас. Цю відповідь я залишив тут як потенційну цікаву для LINQ об’єкти.
Як не дивно, вам не потрібно насправді рахувати. Однак вам потрібно отримати кожен елемент, якщо ви не отримаєте рахунок.
Що ви можете зробити, це зберегти уявлення про "поточне" значення та кількість поточних. Коли ви отримаєте наступне значення, візьміть випадкове число і замініть "струм" на "новий" з ймовірністю 1 / n, де n - це число.
Отже, читаючи перше значення, ви завжди робите це "поточне" значення. Коли ви читаєте друге значення, ви могли б зробити , що поточне значення (ймовірність 1/2). Коли ви читаєте третє значення, ви могли б зробити , що поточне значення (ймовірність 1/3) і т.д. Коли ви біжите з даних, поточне значення є випадковою один з усіх тих , які ви читаєте, з равновероятно.
Щоб застосувати це до умови, просто ігноруйте все, що не відповідає умові. Найпростіший спосіб зробити це - лише розглянути послідовність «узгодження» для початку, застосувавши спочатку пункт «Де».
Ось швидка реалізація. Я думаю, що це нормально ...
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;
}
current
буде завжди бути встановлений на перший елемент. Під час другої ітерації є 50% -на зміна, що вона буде встановлена на другий елемент. На третій ітерації є 33% шансів, що вона буде встановлена на третій елемент. Додавання заяви про перерву означатиме, що ви завжди будете виходити, прочитавши перший елемент, зробивши його зовсім не випадковим.
Один із способів досягти ефективності - додати стовпчик до даних Shuffle
, заповнений випадковим int (як створюється кожен запис).
Частковий запит на доступ до таблиці у випадковому порядку - це ...
Random random = new Random();
int seed = random.Next();
result = result.OrderBy(s => (~(s.Shuffle & seed)) & (s.Shuffle | seed)); // ^ seed);
Це робить операцію XOR в базі даних та замовлення за результатами цього XOR.
Переваги: -
Це підхід, який використовується моєю системою домашньої автоматизації для рандомізації списків відтворення. Він збирає нове насіння щодня, даючи послідовне замовлення протягом дня (що дозволяє легко зупинити / відновити можливості), але свіжий погляд на кожен список відтворення кожного нового дня.
result = result.OrderBy(s => s.Shuffle ^ seed);
(тобто не потрібно реалізовувати XOR через операторів ~, & та |).
якщо ви хочете отримати, наприклад, var count = 16
випадкові рядки з таблиці, ви можете написати
var rows = Table.OrderBy(t => Guid.NewGuid())
.Take(count);
тут я використав EF, а Таблиця - це Dbset
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();
Пояснення: Вставивши довідник (який є випадковим), порядок із замовленням буде випадковим.
Сюди прийшло цікаво, як отримати кілька випадкових сторінок з невеликої їх кількості, тому кожен користувач отримує кілька різних випадкових 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
Ймовірно, слід отримати кілька профілів, перш ніж запитувати велику кількість результатів, але це ідеально підходить для моєї мети
У мене є випадковий запит функції щодо DataTable
s:
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;
}
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);
Щоб завершити, ось як це реалізувати в контексті FAKE даних (який використовується в об'єктах пам'яті):
public IOrderedQueryable<T> Randomize<T>(IQueryable<T> query)
{
return query.OrderBy(x => Guid.NewGuid());
}