लाइनक से Sql के लिए यादृच्छिक पंक्ति


112

जब मेरे पास कोई शर्त हो, तो Linq से SQL का उपयोग करके एक यादृच्छिक पंक्ति को पुनः प्राप्त करने का सबसे अच्छा (और सबसे तेज़) तरीका क्या है, उदाहरण के लिए कुछ फ़ील्ड सही होना चाहिए?


आपके पास सही परिस्थितियों की जांच के लिए दो विकल्प हैं। यदि अधिकांश वस्तुओं पर सही स्थिति होती है, तो बस एक यादृच्छिक आइटम को पकड़ो और फिर परीक्षण करें और झूठे को दोहराएं। यदि दुर्लभ डेटाबेस को विकल्प को वास्तविक स्थिति तक सीमित करता है और फिर यादृच्छिक पर एक को पकड़ो।
रेक्स लोगन

1
इस साइट पर बहुत सारे जवाबों के साथ - दूसरे-रेटेड को स्वीकृत एक की तुलना में बहुत बेहतर है।
nikib3ro

जवाबों:


169

आप एक नकली यूडीएफ का उपयोग करके, डेटाबेस में ऐसा कर सकते हैं; एक आंशिक वर्ग में, डेटा संदर्भ के लिए एक विधि जोड़ें:

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

3
यदि यह फ़िल्टर के बाद 30k है , तो मैं कहूंगा कि इस दृष्टिकोण का उपयोग न करें। 2 राउंड-ट्रिप करें; 1 गणना प्राप्त करने के लिए (), और 1 एक यादृच्छिक पंक्ति पाने के लिए ...
मार्क Gravell

1
क्या होगा यदि आप पाँच (या "x") यादृच्छिक पंक्तियाँ चाहते हैं? क्या यह केवल छह दौर की यात्राएं करना सबसे अच्छा है या संग्रहीत प्रक्रिया में इसे लागू करने का एक सुविधाजनक तरीका है?
नील स्टबलेन

2
@ नील एस .: ctx.Random द्वारा आदेश (टेक 5) के साथ मिलाया जा सकता है; लेकिन यदि आप गणना () दृष्टिकोण का उपयोग कर रहे हैं, तो मुझे उम्मीद है कि 6 दौर की यात्रा सबसे सरल विकल्प है।
मार्क Gravell

1
System.Data.Linq या System.Data.Linq.Mapping.Function फ़ीचर अभ्यस्त काम का संदर्भ जोड़ना न भूलें।
जगुआर

8
मुझे पता है कि यह पुराना है, लेकिन यदि आप एक बड़ी तालिका से कई यादृच्छिक पंक्तियों का चयन कर रहे हैं, तो यह देखें: msdn.microsoft.com/en-us/library/cc441928.aspx मुझे नहीं पता कि कोई LINQ समतुल्य है।
jwd

60

इकाई ढांचे के लिए एक और नमूना:

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

यह LINQ से SQL में काम नहीं करता है। OrderByबस गिरा दिया जा रहा है।


4
क्या आपने इसे प्रमाणित किया है और पुष्टि की है कि यह काम करता है? LINQPad का उपयोग करके मेरे परीक्षणों में, खंड द्वारा आदेश को गिराया जा रहा है।
जिम वोलेई

इस समस्या का यह सबसे अच्छा समाधान है
4thelasers

8
यह LINQ को SQL में काम नहीं करता है ... शायद यह Entity Framework 4 में काम करता है (इसकी पुष्टि नहीं करता है)। यदि आप किसी सूची को क्रमबद्ध कर रहे हैं तो आप केवल .OrderBy का उपयोग कर सकते हैं ... DB के साथ यह काम नहीं करेगा।
nikib3ro

2
बस अंत में पुष्टि करने के लिए कि यह ईएफ 4 में काम करता है - यह उस मामले में बहुत अच्छा विकल्प है।
nikib3ro

1
क्या आप अपने उत्तर को संपादित कर सकते हैं और बता सकते हैं कि एक नए गाइड के साथ ऑर्डर क्यों किया जाता है? इस तरह से अच्छा जवाब :)
जीन-फ्रांकोइस कोटे

32

संपादित करें: मैंने केवल देखा है कि यह 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;
}

4
FYI करें - मैंने एक त्वरित जांच चलाई और इस फ़ंक्शन में एक समान संभाव्यता वितरण है (वेतन वृद्धि गिनती अनिवार्य रूप से फिशर-येट्स फेरबदल के समान है क्योंकि यह उचित लगता है कि यह होना चाहिए)।
ग्रेग बीच

@Greg: कूल, धन्यवाद। त्वरित जाँच से यह मुझे ठीक लगा, लेकिन इस तरह कोड में एक-के-बाद की त्रुटियों को प्राप्त करना इतना आसान है। वस्तुतः LINQ से SQL के लिए अप्रासंगिक है, लेकिन फिर भी उपयोगी है।
जॉन स्कीट

@JonSkeet, हाय, क्या आप यह जाँच कर सकते हैं और मुझे बता सकते हैं कि मैं क्या याद कर रहा हूँ
shaijut

@ टायलरलैंगिंग: नहीं, ब्रेक का मतलब नहीं है। पहली यात्रा पर, currentहोगा हमेशा पहला तत्व के लिए सेट किया। दूसरे पुनरावृत्ति पर, 50% परिवर्तन है कि इसे दूसरे तत्व पर सेट किया जाएगा। तीसरे पुनरावृत्ति पर, 33% संभावना है कि इसे तीसरे तत्व पर सेट किया जाएगा। ब्रेक स्टेटमेंट जोड़ने का मतलब होगा कि आप पहले तत्व को पढ़ने के बाद हमेशा बाहर निकलेंगे, जिससे यह बिल्कुल यादृच्छिक नहीं होगा।
जॉन स्कीट

@JonSkeet दोह! मैंने आपकी गणना के उपयोग को गलत बताया (जैसे सोच रहा था कि यह फ़िशर-येट्स शैली है जैसे नी के साथ एक यादृच्छिक रेंज)। लेकिन फिशर-येट्स में पहला तत्व चुनने के लिए किसी भी तत्व को काफी चुनना है। हालांकि, तत्वों की कुल संख्या को जानने की आवश्यकता है। मैं अब देखता हूं कि आपका समाधान एक IEnumerable के लिए साफ है कि कुल गिनती ज्ञात नहीं है, और पूरे स्रोत पर पुनरावृत्ति करने की आवश्यकता नहीं है बस गिनती प्राप्त करने के लिए, फिर से कुछ बेतरतीब ढंग से चुने गए सूचकांक में पुन: दर्ज करें। बल्कि यह एक पास में हल करता है, जैसा कि आपने कहा था: "हर तत्व को लाने की जरूरत है जब तक कि आपको गिनती न मिले"।
टायलर Laing

19

कुशलता से प्राप्त करने का एक तरीका Shuffleयह है कि आप अपने डेटा में एक कॉलम जोड़ें जो एक यादृच्छिक इंट (प्रत्येक रिकॉर्ड बनाया जाता है) के साथ आबाद है।

यादृच्छिक क्रम में तालिका तक पहुँचने के लिए आंशिक क्वेरी है ...

Random random = new Random();
int seed = random.Next();
result = result.OrderBy(s => (~(s.Shuffle & seed)) & (s.Shuffle | seed)); // ^ seed);

यह डेटाबेस में एक XOR ऑपरेशन करता है और उस XOR के परिणामों द्वारा आदेश देता है।

लाभ: -

  1. कुशल: SQL ऑर्डर को संभालता है, पूरी तालिका लाने की कोई आवश्यकता नहीं है
  2. दोहराने योग्य: (परीक्षण के लिए अच्छा) - समान यादृच्छिक क्रम उत्पन्न करने के लिए एक ही यादृच्छिक बीज का उपयोग कर सकते हैं

यह मेरे घर स्वचालन प्रणाली द्वारा प्लेलिस्ट को यादृच्छिक बनाने के लिए उपयोग किया जाता है। यह प्रत्येक दिन एक नया बीज चुनता है जो दिन के दौरान एक सुसंगत क्रम देता है (आसान ठहराव / फिर से शुरू करने की क्षमता की अनुमति देता है) लेकिन प्रत्येक नए दिन में प्रत्येक प्लेलिस्ट पर एक नया रूप।


रैंडमनेस पर क्या असर होगा अगर आप एक रैंडम इंट फील्ड को जोड़ने के बजाय सिर्फ एक मौजूदा ऑटो-इन्क्रिमेंटिंग आइडेंटिटी फील्ड का इस्तेमाल करते हैं (बीज जाहिर तौर पर रैंडम रहेगा)? यह भी - तालिका में रिकॉर्ड की संख्या के बराबर आ के साथ एक बीज मूल्य पर्याप्त है या यह अधिक होना चाहिए?
ब्रायन

सहमत, यह एक महान जवाब है जिसे आईएमओ को अधिक उत्थान करना चाहिए। मैंने इसका उपयोग एक एंटिटी फ्रेमवर्क क्वेरी में किया था, और बिटवाइज़-एक्सओआर ऑपरेटर ^ को काम करने के लिए सीधे लगता है कि इस स्थिति को थोड़ा क्लीनर बना दिया है: result = result.OrderBy(s => s.Shuffle ^ seed);(यानी ~, और | और ऑपरेटरों के माध्यम से एक्सओआर को लागू करने की कोई आवश्यकता नहीं है)।
स्टीवन रेंड्स

7

यदि आप var count = 16तालिका से यादृच्छिक पंक्तियाँ प्राप्त करना चाहते हैं , तो आप लिख सकते हैं

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

यहाँ मैंने EF का उपयोग किया है, और तालिका एक Dbset है


1

यदि यादृच्छिक पंक्तियों को प्राप्त करने का उद्देश्य नमूना है, तो मैंने बहुत संक्षेप में यहां लार्सन एट अल। माइक्रोसॉफ्ट रिसर्च टीम से एक अच्छे दृष्टिकोण के बारे में बात की है , जहां उन्होंने भौतिक विचारों का उपयोग करके एसक्यूएल सर्वर के लिए एक नमूना रूपरेखा विकसित की है। वास्तविक पेपर के लिए एक लिंक भी है।


1
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();

स्पष्टीकरण: गाइड को सम्मिलित करके (जो यादृच्छिक है) ऑर्डरबी के साथ क्रम यादृच्छिक होगा।


लोग "यादृच्छिक" नहीं हैं वे गैर-अनुक्रमिक हैं। इसमे अंतर है। व्यवहार में यह संभव है कि कुछ तुच्छ के लिए यह मायने नहीं रखता है।
क्रिस मैरिकिक

0

यहाँ आया था कि उनमें से कम संख्या में कुछ यादृच्छिक पेज कैसे प्राप्त करें, इसलिए प्रत्येक उपयोगकर्ता को कुछ अलग यादृच्छिक 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

संभवतः परिणामों की एक बड़ी संख्या को क्वेरी करने से पहले कुछ रूपरेखा प्राप्त करनी चाहिए, लेकिन यह मेरे उद्देश्य के लिए एकदम सही है


0

मेरे पास DataTables के विरुद्ध यादृच्छिक फ़ंक्शन क्वेरी है :

var result = (from result in dt.AsEnumerable()
              order by Guid.NewGuid()
              select result).Take(3); 

0

नीचे दिया गया उदाहरण एक गणना को पुनः प्राप्त करने के लिए स्रोत को कॉल करेगा और फिर 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);
    }
}

कुछ स्पष्टीकरण अच्छा होगा
एंड्रयू बार्बर

यह कोड थ्रेडसेफ़ नहीं है और इसे केवल सिंगल थ्रेडेड कोड में उपयोग किया जा सकता है (इसलिए ASP.NET नहीं )
क्रिस मैरिसिक

0

मैं इस विधि का उपयोग यादृच्छिक समाचार और इसके काम को ठीक करने के लिए करता हूं;)

    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;
    }

0

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()

0

यदि आप LINQPad का उपयोग करते हैं , तो C # प्रोग्राम मोड में स्विच करें और इस तरह करें:

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

[Function(Name = "NEWID", IsComposable = true)]
public Guid Random()
{
    throw new NotImplementedException();
}


0

मार्क ग्रेवेल के समाधान में जोड़ने के लिए। यदि आप स्वयं डेटाकोटेक्स्ट क्लास के साथ काम नहीं कर रहे हैं (क्योंकि आप इसे किसी भी तरह से प्रॉक्सी करते हैं, जैसे कि परीक्षण उद्देश्यों के लिए डेटाकोटेक्स्ट को नकली करना), तो आप सीधे परिभाषित यूडीएफ का उपयोग नहीं कर सकते हैं: यह एसक्यूएल के लिए संकलित नहीं किया जाएगा क्योंकि आप इसमें उपयोग नहीं कर रहे हैं आपके वास्तविक डेटा संदर्भ वर्ग का उपवर्ग या आंशिक वर्ग।

इस समस्या के लिए वर्कअराउंड आपके प्रॉक्सी में एक रैंडमाइज फंक्शन बनाना है, इसे उस क्वेरी के साथ फीड करें जिसे आप रैंडमाइज करना चाहते हैं:

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());
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.