Cách tốt nhất (và nhanh nhất) để truy xuất một hàng ngẫu nhiên bằng Linq to SQL là gì khi tôi có một điều kiện, ví dụ như trường nào đó phải đúng?
Cách tốt nhất (và nhanh nhất) để truy xuất một hàng ngẫu nhiên bằng Linq to SQL là gì khi tôi có một điều kiện, ví dụ như trường nào đó phải đúng?
Câu trả lời:
Bạn có thể thực hiện việc này tại cơ sở dữ liệu, bằng cách sử dụng UDF giả mạo; trong một lớp từng phần, hãy thêm một phương thức vào ngữ cảnh dữ liệu:
partial class MyDataContext {
[Function(Name="NEWID", IsComposable=true)]
public Guid Random()
{ // to prove not used by our C# code...
throw new NotImplementedException();
}
}
Sau đó, chỉ cần order by ctx.Random()
; điều này sẽ thực hiện một thứ tự ngẫu nhiên theo sự cho phép của SQL-Server NEWID()
. I E
var cust = (from row in ctx.Customers
where row.IsActive // your filter
orderby ctx.Random()
select row).FirstOrDefault();
Lưu ý rằng điều này chỉ phù hợp với các bàn có kích thước từ nhỏ đến trung bình; đối với các bảng lớn, nó sẽ có tác động đến hiệu suất tại máy chủ và sẽ hiệu quả hơn khi tìm số hàng ( Count
), sau đó chọn ngẫu nhiên một hàng ( Skip/First
).
đối với phương pháp đếm:
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
Một mẫu khác cho Khung thực thể:
var customers = db.Customers
.Where(c => c.IsActive)
.OrderBy(c => Guid.NewGuid())
.FirstOrDefault();
Điều này không hoạt động với LINQ to SQL. Đơn OrderBy
giản là bị bỏ.
CHỈNH SỬA: Tôi chỉ nhận thấy đây là LINQ to SQL, không phải LINQ to Object. Sử dụng mã của Marc để lấy cơ sở dữ liệu thực hiện việc này cho bạn. Tôi đã để câu trả lời này ở đây như một điểm quan tâm tiềm năng cho LINQ đối với các Đối tượng.
Thật kỳ lạ, bạn thực sự không cần phải đếm. Tuy nhiên, bạn cần tìm nạp mọi phần tử trừ khi bạn nhận được số lượng.
Những gì bạn có thể làm là giữ ý tưởng về giá trị "hiện tại" và số lượng hiện tại. Khi bạn tìm nạp giá trị tiếp theo, hãy lấy một số ngẫu nhiên và thay thế "hiện tại" bằng "mới" với xác suất 1 / n trong đó n là số đếm.
Vì vậy, khi bạn đọc giá trị đầu tiên, bạn luôn đặt đó là giá trị "hiện tại". Khi bạn đọc giá trị thứ hai, bạn có thể biến nó thành giá trị hiện tại (xác suất 1/2). Khi bạn đọc giá trị thứ ba, bạn có thể đặt giá trị đó thành giá trị hiện tại (xác suất 1/3), v.v. Khi bạn dùng hết dữ liệu, giá trị hiện tại là một giá trị ngẫu nhiên trong số tất cả những giá trị bạn đọc, với xác suất đồng nhất.
Để áp dụng điều đó với một điều kiện, chỉ cần bỏ qua bất kỳ điều gì không đáp ứng điều kiện. Cách dễ nhất để làm điều đó là chỉ xem xét trình tự "khớp" để bắt đầu, bằng cách áp dụng mệnh đề Where trước.
Đây là một triển khai nhanh chóng. Tôi nghĩ nó ổn ...
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
sẽ luôn được đặt thành phần tử đầu tiên. Ở lần lặp thứ hai, có một sự thay đổi 50% là nó sẽ được đặt thành phần tử thứ hai. Ở lần lặp thứ ba, có 33% khả năng nó sẽ được đặt thành phần tử thứ ba. Thêm một câu lệnh break có nghĩa là bạn sẽ luôn thoát ra sau khi đọc phần tử đầu tiên, khiến nó không hề ngẫu nhiên.
Một cách để đạt được hiệu quả là thêm một cột vào dữ liệu của bạn Shuffle
được điền bằng một số nguyên ngẫu nhiên (khi mỗi bản ghi được tạo).
Truy vấn từng phần để truy cập bảng theo thứ tự ngẫu nhiên là ...
Random random = new Random();
int seed = random.Next();
result = result.OrderBy(s => (~(s.Shuffle & seed)) & (s.Shuffle | seed)); // ^ seed);
Điều này thực hiện một hoạt động XOR trong cơ sở dữ liệu và sắp xếp theo kết quả của XOR đó.
Ưu điểm: -
Đây là phương pháp được hệ thống tự động hóa tại nhà của tôi sử dụng để ngẫu nhiên hóa danh sách phát. Nó chọn một hạt giống mới mỗi ngày cho một thứ tự nhất quán trong ngày (cho phép khả năng tạm dừng / tiếp tục dễ dàng) nhưng một cái nhìn mới về từng danh sách phát mỗi ngày mới.
result = result.OrderBy(s => s.Shuffle ^ seed);
(tức là không cần triển khai XOR thông qua các toán tử ~, & và |).
nếu bạn muốn lấy var count = 16
các hàng ngẫu nhiên từ bảng, bạn có thể viết
var rows = Table.OrderBy(t => Guid.NewGuid())
.Take(count);
ở đây tôi đã sử dụng EF và Bảng là một Dbset
Nếu mục đích của việc lấy các hàng ngẫu nhiên là lấy mẫu, thì tôi đã nói rất ngắn gọn ở đây về một cách tiếp cận hay từ Larson và cộng sự, nhóm Nghiên cứu của Microsoft, nơi họ đã phát triển khung lấy mẫu cho Sql Server bằng cách sử dụng các khung nhìn cụ thể hóa. Cũng có một liên kết đến các bài báo thực tế.
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();
Giải thích: Bằng cách chèn hướng dẫn (là ngẫu nhiên), thứ tự với orderby sẽ là ngẫu nhiên.
Đến đây tự hỏi làm thế nào để lấy một vài trang ngẫu nhiên từ một số ít trong số đó, vì vậy mỗi người dùng nhận được một số 3 trang ngẫu nhiên khác nhau.
Đây là giải pháp cuối cùng của tôi, làm việc truy vấn với LINQ dựa trên danh sách các trang trong Sharepoint 2010. Nó nằm trong Visual Basic, xin lỗi: p
Dim Aleatorio As New Random()
Dim Paginas = From a As SPListItem In Sitio.RootWeb.Lists("Páginas") Order By Aleatorio.Next Take 3
Có lẽ nên lấy một số hồ sơ trước khi truy vấn một số lượng lớn kết quả, nhưng nó hoàn hảo cho mục đích của tôi
Tôi có truy vấn hàm ngẫu nhiên đối với DataTable
s:
var result = (from result in dt.AsEnumerable()
order by Guid.NewGuid()
select result).Take(3);
Ví dụ dưới đây sẽ gọi nguồn để lấy số đếm và sau đó áp dụng biểu thức bỏ qua trên nguồn với một số từ 0 đến n. Phương pháp thứ hai sẽ áp dụng thứ tự bằng cách sử dụng đối tượng ngẫu nhiên (sẽ sắp xếp mọi thứ trong bộ nhớ) và chọn số được truyền vào lệnh gọi phương thức.
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);
}
}
tôi sử dụng phương pháp này để lấy tin tức ngẫu nhiên và nó hoạt động tốt;)
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);
Chọn hàng ngẫu nhiên 2
Để thêm vào giải pháp của Marc Gravell. Nếu bạn không làm việc với chính lớp datacontext (vì bạn ủy quyền nó bằng cách nào đó, ví dụ: giả mạo văn bản dữ liệu cho mục đích thử nghiệm), bạn không thể sử dụng trực tiếp UDF đã xác định: nó sẽ không được biên dịch sang SQL vì bạn không sử dụng nó trong lớp con hoặc lớp một phần của lớp ngữ cảnh dữ liệu thực của bạn.
Một giải pháp cho vấn đề này là tạo một hàm Randomize trong proxy của bạn, cung cấp nó bằng truy vấn bạn muốn được ngẫu nhiên hóa:
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());
}
}
Đây là cách bạn sử dụng nó trong mã của mình:
var query = _dc.Repository<SomeEntity>();
query = _dc.Randomize(query);
Để hoàn thành, đây là cách thực hiện điều này trong văn bản dữ liệu FAKE (sử dụng trong các thực thể bộ nhớ):
public IOrderedQueryable<T> Randomize<T>(IQueryable<T> query)
{
return query.OrderBy(x => Guid.NewGuid());
}