Losowy wiersz z Linq do Sql

Jaki jest najlepszy (i najszybszy) sposób na pobranie losowego wiersza za pomocą Linq do SQL, gdy mam warunek, np. jakieś pole musi być prawdziwe?

Author: Cœur, 2009-03-15

15 answers

Możesz to zrobić w bazie danych, używając fałszywego UDF; w częściowej klasie dodaj metodę do kontekstu danych:

partial class MyDataContext {
     [Function(Name="NEWID", IsComposable=true)] 
     public Guid Random() 
     { // to prove not used by our C# code... 
         throw new NotImplementedException(); 
     }
}

Następnie po prostu order by ctx.Random(); spowoduje to losowe zamówienie na serwerze SQL dzięki uprzejmości NEWID(). tj.

var cust = (from row in ctx.Customers
           where row.IsActive // your filter
           orderby ctx.Random()
           select row).FirstOrDefault();

Zauważ, że jest to odpowiednie tylko dla małych i średnich tabel; w przypadku dużych tabel będzie to miało wpływ na wydajność na serwerze i będzie bardziej efektywne znalezienie liczby wierszy (Count), a następnie wybranie jednego losowo (Skip/First).


Dla liczenia podejście:

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
 165
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-24 19:16:24

Kolejna próbka dla struktury encji:

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

To nie działa z LINQ do SQL. OrderBy jest po prostu wyrzucany.

 58
Author: Konstantin Tarkus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-23 16:26:18

EDIT: dopiero co zauważyłem, że jest to LINQ do SQL, a nie LINQ do obiektów. Użyj kodu marca, żeby baza danych zrobiła to za Ciebie. Zostawiłam tę odpowiedź tutaj jako potencjalny punkt zainteresowania dla LINQ to Objects.

O dziwo, nie musisz liczyć. Musisz jednak pobrać każdy element, chyba że otrzymasz liczbę.

Możesz zachować ideę "bieżącej" wartości i bieżącej liczby. Po pobraniu kolejnej wartości, weź losową liczbę i zastąp " bieżący "na " nowy"z prawdopodobieństwem 1 / n, gdzie n jest liczbą.

Więc kiedy czytasz pierwszą wartość, zawsze tworzysz" bieżącą " wartość. Po odczytaniu drugiej wartości, Może sprawić, że bieżąca wartość (prawdopodobieństwo 1/2). Po odczytaniu trzeciej wartości, Może sprawić, że bieżąca wartość (prawdopodobieństwo 1/3) itd. Gdy zabraknie danych, aktualna wartość jest losowa ze wszystkich przeczytanych, z jednolitym prawdopodobieństwo.

Aby zastosować to z warunkiem, po prostu zignoruj wszystko, co nie spełnia warunku. Najprostszym sposobem na to jest rozważenie tylko sekwencji" dopasowania", od której należy zacząć, poprzez zastosowanie klauzuli Where.

Oto szybka realizacja. W porządku...

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;
}
 28
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-15 18:12:45

Jednym ze sposobów skutecznego osiągnięcia jest dodanie kolumny do danych Shuffle, która jest wypełniona losowo int(jak każdy rekord jest tworzony).

Częściowe Zapytanie o dostęp do tabeli w losowej kolejności to ...

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

To wykonuje operację XOR w bazie danych i rozkazuje przez Wyniki tego XOR.

Zalety: -

  1. efektywny: SQL obsługuje zamawiania, nie trzeba pobierać całego tabela
  2. powtarzalne: (dobre dla testowania) - można użyć tego samego losowe seed, aby wygenerować ten sam losowy zamówienie

To jest podejście używane przez mój system automatyki domowej do losowego losowania list odtwarzania. Każdego dnia wybiera nowe ziarno, dając spójną kolejność w ciągu dnia (umożliwiając łatwe wstrzymywanie / wznawianie), ale świeże spojrzenie na każdą playlistę każdego nowego dnia.

 17
Author: Ian Mercer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-27 15:32:58

Jeśli chcesz uzyskać np. var count = 16 losowe wiersze z tabeli, możesz napisać

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

Tutaj użyłem E. F, A Tabela to Dbset

 7
Author: Artur Keyan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-03 06:18:41

Jeśli celem uzyskania losowych wierszy jest próbkowanie, rozmawiałem bardzo krótko tutaj o miłym podejściu od Larson et al., Microsoft Research team, gdzie opracowali Framework próbkowania dla SQL Server przy użyciu zmaterializowanych widoków. Jest też link do aktualnego artykułu.

 1
Author: naiemk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-11 14:32:42

Przyszedł tutaj zastanawiając się, jak uzyskać kilka losowych stron z niewielkiej liczby z nich, więc każdy użytkownik dostaje kilka różnych losowych stron 3.

To jest moje ostateczne rozwiązanie, praca z zapytaniem LINQ na liście stron w Sharepoint 2010. Jest w Visual Basicu, sorry: P

Dim Aleatorio As New Random()

Dim Paginas = From a As SPListItem In Sitio.RootWeb.Lists("Páginas") Order By Aleatorio.Next Take 3

Prawdopodobnie powinno się zrobić profilowanie przed zapytaniem o dużą liczbę wyników, ale jest to idealne dla mojego celu

 1
Author: Fran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-22 12:54:49
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();

Wyjaśnienie: wstawiając guid (który jest losowy) kolejność z orderby byłaby losowa.

 1
Author: Nayeem Mansoori,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-23 06:05:44

I have random function query against DataTables:

var result = (from result in dt.AsEnumerable()
              order by Guid.NewGuid()
              select result).Take(3); 
 0
Author: midhun sankar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 16:46:46

Poniższy przykład wywoła źródło, aby pobrać liczbę, a następnie zastosuje wyrażenie pomiń na źródle z liczbą od 0 do N. druga metoda zastosuje kolejność za pomocą obiektu losowego (który uporządkuje wszystko w pamięci) i wybierze liczbę przekazaną do wywołania metody.

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);
    }
}
 0
Author: user1619860,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-09 23:12:01

Używam tej metody do pobierania losowych Newsów i jej działa dobrze;)

    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
Author: sadati,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-24 01:11:49

Używanie LINQ do SQL w LINQPad jako instrukcji C# wygląda jak

IEnumerable<Customer> customers = this.ExecuteQuery<Customer>(@"SELECT top 10 * from [Customers] order by newid()");
customers.Dump();

Wygenerowany SQL to

SELECT top 10 * from [Customers] order by newid()
 0
Author: JCO,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-10 09:21:25

Jeśli używasz LINQPad, przełącz się na tryb C# program i zrób to w ten sposób:

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

[Function(Name = "NEWID", IsComposable = true)]
public Guid Random()
{
    throw new NotImplementedException();
}
 0
Author: alexey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-13 16:10:54
var cust = (from c in ctx.CUSTOMERs.ToList() select c).OrderBy(x => x.Guid.NewGuid()).Taket(2);

Wybierz losowy 2 wiersz

 0
Author: Bahadır ATASOY,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-25 22:18:25

Aby dodać do rozwiązania marca Gravella. Jeśli nie pracujesz z samą klasą datacontext (ponieważ zastępujesz ją w jakiś sposób, np. aby sfałszować datacontext w celach testowych), nie możesz użyć zdefiniowanego UDF bezpośrednio: nie zostanie skompilowany do SQL, ponieważ nie używasz go w podklasie lub częściowej klasie rzeczywistej klasy kontekstu danych.

Obejściem tego problemu jest utworzenie funkcji Randomize w serwerze proxy, podając ją zapytaniem, które chcesz randomizowane:

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

Oto jak byś go użył w swoim kodzie:

var query = _dc.Repository<SomeEntity>();
query = _dc.Randomize(query);

Aby być kompletnym, oto jak zaimplementować to w FAKE datacontext (który używa w encjach pamięci):

public IOrderedQueryable<T> Randomize<T>(IQueryable<T> query)
{
    return query.OrderBy(x => Guid.NewGuid());
}
 0
Author: Dave de Jong,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-08 13:44:29