जब मेरे पास कोई शर्त हो, तो Linq से SQL का उपयोग करके एक यादृच्छिक पंक्ति को पुनः प्राप्त करने का सबसे अच्छा (और सबसे तेज़) तरीका क्या है, उदाहरण के लिए कुछ फ़ील्ड सही होना चाहिए?
जब मेरे पास कोई शर्त हो, तो Linq से SQL का उपयोग करके एक यादृच्छिक पंक्ति को पुनः प्राप्त करने का सबसे अच्छा (और सबसे तेज़) तरीका क्या है, उदाहरण के लिए कुछ फ़ील्ड सही होना चाहिए?
जवाबों:
आप एक नकली यूडीएफ का उपयोग करके, डेटाबेस में ऐसा कर सकते हैं; एक आंशिक वर्ग में, डेटा संदर्भ के लिए एक विधि जोड़ें:
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
इकाई ढांचे के लिए एक और नमूना:
var customers = db.Customers
.Where(c => c.IsActive)
.OrderBy(c => Guid.NewGuid())
.FirstOrDefault();
यह LINQ से SQL में काम नहीं करता है। OrderBy
बस गिरा दिया जा रहा है।
संपादित करें: मैंने केवल देखा है कि यह LINQ to SQL है, LINQ ऑब्जेक्ट्स के लिए नहीं। आपके लिए ऐसा करने के लिए डेटाबेस प्राप्त करने के लिए मार्क कोड का उपयोग करें। मैंने यह उत्तर यहाँ LINQ for Objects के लिए एक संभावित बिंदु के रूप में छोड़ा है।
अजीब तरह से, आपको वास्तव में गिनती प्राप्त करने की आवश्यकता नहीं है। हालाँकि, आपको हर तत्व प्राप्त करने की आवश्यकता होती है जब तक कि आपको गिनती न मिले।
आप जो कर सकते हैं वह "वर्तमान" मान और वर्तमान गणना का विचार रख सकता है। जब आप अगला मूल्य प्राप्त करते हैं, तो एक यादृच्छिक संख्या लें और "चालू" को 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
यह है कि आप अपने डेटा में एक कॉलम जोड़ें जो एक यादृच्छिक इंट (प्रत्येक रिकॉर्ड बनाया जाता है) के साथ आबाद है।
यादृच्छिक क्रम में तालिका तक पहुँचने के लिए आंशिक क्वेरी है ...
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);
(यानी ~, और | और ऑपरेटरों के माध्यम से एक्सओआर को लागू करने की कोई आवश्यकता नहीं है)।
यदि यादृच्छिक पंक्तियों को प्राप्त करने का उद्देश्य नमूना है, तो मैंने बहुत संक्षेप में यहां लार्सन एट अल। माइक्रोसॉफ्ट रिसर्च टीम से एक अच्छे दृष्टिकोण के बारे में बात की है , जहां उन्होंने भौतिक विचारों का उपयोग करके एसक्यूएल सर्वर के लिए एक नमूना रूपरेखा विकसित की है। वास्तविक पेपर के लिए एक लिंक भी है।
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 पृष्ठ मिलते हैं।
यह मेरा अंतिम समाधान है, Sharepoint 2010 में पृष्ठों की एक सूची के खिलाफ LINQ के साथ काम करना। यह विज़ुअल बेसिक में है, क्षमा करें, पी।
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;
}
LINQPad में 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()
var cust = (from c in ctx.CUSTOMERs.ToList() select c).OrderBy(x => x.Guid.NewGuid()).Taket(2);
यादृच्छिक 2 पंक्ति का चयन करें
मार्क ग्रेवेल के समाधान में जोड़ने के लिए। यदि आप स्वयं डेटाकोटेक्स्ट क्लास के साथ काम नहीं कर रहे हैं (क्योंकि आप इसे किसी भी तरह से प्रॉक्सी करते हैं, जैसे कि परीक्षण उद्देश्यों के लिए डेटाकोटेक्स्ट को नकली करना), तो आप सीधे परिभाषित यूडीएफ का उपयोग नहीं कर सकते हैं: यह एसक्यूएल के लिए संकलित नहीं किया जाएगा क्योंकि आप इसमें उपयोग नहीं कर रहे हैं आपके वास्तविक डेटा संदर्भ वर्ग का उपवर्ग या आंशिक वर्ग।
इस समस्या के लिए वर्कअराउंड आपके प्रॉक्सी में एक रैंडमाइज फंक्शन बनाना है, इसे उस क्वेरी के साथ फीड करें जिसे आप रैंडमाइज करना चाहते हैं:
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());
}