Bir koşulum olduğunda, Linq to SQL kullanarak rastgele bir satırı almanın en iyi (ve en hızlı) yolu nedir, örneğin bazı alanların doğru olması gerekir?
Bir koşulum olduğunda, Linq to SQL kullanarak rastgele bir satırı almanın en iyi (ve en hızlı) yolu nedir, örneğin bazı alanların doğru olması gerekir?
Yanıtlar:
Bunu, sahte bir UDF kullanarak veritabanında yapabilirsiniz; kısmi bir sınıfta, veri bağlamına bir yöntem ekleyin:
partial class MyDataContext {
[Function(Name="NEWID", IsComposable=true)]
public Guid Random()
{ // to prove not used by our C# code...
throw new NotImplementedException();
}
}
O zaman sadece order by ctx.Random()
; bu, SQL-Server'ın izniyle rastgele bir sıralama yapacaktır NEWID()
. yani
var cust = (from row in ctx.Customers
where row.IsActive // your filter
orderby ctx.Random()
select row).FirstOrDefault();
Bunun yalnızca küçük ve orta büyüklükteki tablolar için uygun olduğunu unutmayın; büyük tablolar için, sunucuda performans etkisi olacaktır ve satır sayısını ( Count
) bulmak ve ardından rastgele birini seçmek ( ) daha verimli olacaktır Skip/First
.
sayma yaklaşımı için:
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 için başka bir örnek:
var customers = db.Customers
.Where(c => c.IsActive)
.OrderBy(c => Guid.NewGuid())
.FirstOrDefault();
Bu LINQ to SQL ile çalışmaz. OrderBy
Basitçe bırakılıyor.
DÜZENLEME: Sadece LINQ to SQL, LINQ to Objects değil fark ettim. Bunu sizin için yapacak veritabanını almak için Marc'ın kodunu kullanın. Bu yanıtı burada LINQ to Objects için potansiyel bir ilgi noktası olarak bıraktım.
Garip bir şekilde, aslında sayıma ihtiyacınız yok. Bununla birlikte, sayımı almadığınız sürece her öğeyi getirmeniz gerekir.
Yapabileceğiniz şey, "güncel" bir değer ve mevcut sayım fikrini korumaktır. Bir sonraki değeri getirdiğinizde, rastgele bir sayı alın ve "mevcut" olanı "yeni" ile değiştirin, 1 / n olasılıkla, burada n sayıdır.
Yani ilk değeri okuduğunuzda, daima "mevcut" değer . Eğer ikinci değer okuduğunuzda belki o anki değeri (olasılık 1/2) yapmak. Üçüncü değer okuduğunuzda belki veri tükendi akım değeri (olasılık 1/3) vb şimdiki değer üniforma olasılıkla, okunan tüm olanlar dışında rastgele biri olduğunu olun.
Bunu bir koşulla uygulamak için, koşulu karşılamayan her şeyi göz ardı edin. Bunu yapmanın en kolay yolu, önce bir Where cümlesi uygulayarak, yalnızca "eşleştirme" dizisinin başlamasını düşünmektir.
İşte hızlı bir uygulama. Ben düşünüyorum sorun yok ...
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
her zaman ilk öğeye ayarlanacaktır. İkinci yinelemede, ikinci öğeye ayarlanacak% 50'lik bir değişiklik var. Üçüncü yinelemede, üçüncü öğeye ayarlanma şansı% 33'tür. Bir break ifadesi eklemek, ilk öğeyi okuduktan sonra her zaman çıkacağınız anlamına gelir, bu da onu hiç rastgele yapmaz.
Verimli bir şekilde başarmanın bir yolu, verilerinize Shuffle
rastgele int ile doldurulmuş bir sütun eklemektir (her kayıt oluşturulurken).
Tabloya rastgele sırayla erişmek için kısmi sorgu ...
Random random = new Random();
int seed = random.Next();
result = result.OrderBy(s => (~(s.Shuffle & seed)) & (s.Shuffle | seed)); // ^ seed);
Bu, veritabanında bir XOR işlemi yapar ve bu XOR'un sonuçlarına göre sıralar.
Avantajları: -
Bu, ev otomasyon sistemimin oynatma listelerini rastgele hale getirmek için kullandığı yaklaşımdır. Her gün, gün boyunca tutarlı bir sipariş veren (kolay duraklatma / devam ettirme yeteneklerine izin veren) ancak her yeni gün her oynatma listesine yeni bir bakış veren yeni bir tohum seçer.
result = result.OrderBy(s => s.Shuffle ^ seed);
(yani XOR'u ~, & ve | operatörleri aracılığıyla uygulamaya gerek yok).
Örneğin var count = 16
tablodan rastgele satırlar almak istiyorsanız, yazabilirsiniz
var rows = Table.OrderBy(t => Guid.NewGuid())
.Take(count);
burada EF kullandım ve Tablo bir Dbset
Rastgele satırlar elde etmenin amacı örnekleme ise, burada kısaca Larson ve diğerlerinin, somutlaştırılmış görünümleri kullanarak Sql Server için bir örnekleme çerçevesi geliştirdikleri Microsoft Araştırma ekibinin güzel bir yaklaşımından bahsetmiştim . Gerçek makaleye de bir bağlantı var.
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();
Açıklama: Rastgele kılavuzun eklenmesi ile, orderby ile sıra rastgele olacaktır.
Buraya, az sayıda rastgele birkaç sayfanın nasıl alınacağını merak etmeye geldim, böylece her kullanıcı farklı rastgele 3 sayfa alır.
Bu benim son çözümüm, Sharepoint 2010'daki bir sayfa listesine karşı LINQ ile sorgulama yaparak çalışıyor. Visual Basic'te, üzgünüm: p
Dim Aleatorio As New Random()
Dim Paginas = From a As SPListItem In Sitio.RootWeb.Lists("Páginas") Order By Aleatorio.Next Take 3
Muhtemelen çok sayıda sonucu sorgulamadan önce biraz profilleme almalıyım, ancak amacım için mükemmel
S'ye karşı rastgele işlev sorgum var DataTable
:
var result = (from result in dt.AsEnumerable()
order by Guid.NewGuid()
select result).Take(3);
Aşağıdaki örnek, kaynağı bir sayımı almak için çağıracak ve ardından 0 ile n arasında bir sayı ile kaynakta bir atlama ifadesi uygulayacaktır. İkinci yöntem, rastgele nesneyi kullanarak (bellekteki her şeyi sıralayacak) sıralamayı uygulayacak ve yöntem çağrısına geçirilen sayıyı seçecektir.
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);
}
}
Bu yöntemi rastgele haberler almak için kullanıyorum ve işi iyi;)
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);
Rastgele 2 sıra seç
Marc Gravell'in çözümüne eklemek için. Veri bağlamı sınıfının kendisiyle çalışmıyorsanız (örneğin, test amacıyla veri bağlamını taklit etmek için ona bir şekilde proxy uyguladığınız için), tanımlanan UDF'yi doğrudan kullanamazsınız: SQL'e derlenmeyecektir çünkü onu bir gerçek veri bağlamı sınıfınızın alt sınıfı veya kısmi sınıfı.
Bu problem için geçici bir çözüm, proxy'nizde bir Randomize işlevi oluşturmak ve onu randomize edilmesini istediğiniz sorgu ile beslemektir:
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());
}
}
Bunu kodunuzda nasıl kullanacağınız aşağıda açıklanmıştır:
var query = _dc.Repository<SomeEntity>();
query = _dc.Randomize(query);
Tam olmak için, bunu FAKE veri bağlamında (bellek varlıklarında kullanılan) şu şekilde uygulayabilirsiniz:
public IOrderedQueryable<T> Randomize<T>(IQueryable<T> query)
{
return query.OrderBy(x => Guid.NewGuid());
}