EF Code First: So erhalten Sie zufällige Zeilen


79

Wie kann ich eine Abfrage erstellen, bei der ich zufällige Zeilen abrufen würde?

Wenn ich es in SQL schreiben würde, würde ich eine Bestellung mit newid () aufgeben und n Zeilen von oben abschneiden. Wie auch immer, um dies zuerst im EF-Code zu tun?

Ich habe versucht, eine Abfrage mit newid () zu erstellen und mit DbSet.SqlQuery () auszuführen. Während es funktioniert, ist es nicht die sauberste Lösung.

Versuchen Sie außerdem, alle Zeilen abzurufen und nach einer neuen Anleitung zu sortieren. Obwohl die Anzahl der Zeilen ziemlich gering ist, ist es immer noch keine gute Lösung.

Irgendwelche Ideen?



Antworten:


162

Ruf einfach an:

something.OrderBy(r => Guid.NewGuid()).Take(5)

Hallo, es funktioniert gut, aber wird dies schnell sein, wenn die Tabelle mehr Zeilen hat
? Ich habe

3
Siehe diese Frage , es ist leider kaputt. Es sieht so OrderByaus, als ob die Rangfolgefunktion stabil ist, was bei einem Zufallsgenerator nicht der Fall ist. Linq to-Entitäten übersetzen dies in eine SQL-Abfrage, die für dieselbe Entität möglicherweise ein anderes Ranking erhält (sobald Ihre Abfragen verwendet werden Include). Dann wird die Entität in der Ergebnisliste dupliziert.
Frédéric

1
Ich bin mir nicht sicher, ob ich dies für Aufgaben vertrauen würde, die einen ironclad-Satz zufälliger Zeilen erfordern - ich würde mich wahrscheinlich stattdessen für stackoverflow.com/a/654910/12484 oder stackoverflow.com/a/648247/12484 entscheiden -, aber für diesen einfachen Ansatz funktionierte gut für meine Bedürfnisse, die eine einzelne Pseudozufallszeile für eine nicht kundenorientierte Funktion erforderten. +1.
Jon Schneider

@Toolkit ist wahrscheinlich nicht so seltsam, wenn Entity kein Oracle-Äquivalent von hat Guid.NewGuid()(dh LinqToSql oder was auch immer das daraus macht, NEWID()aber niemand hat dasselbe für Oracle programmiert).
Drzaus

Ist dieser Ansatz effizient? Ich habe an anderer Stelle festgestellt, dass diese Methode - aus Leistungsgründen - nicht empfohlen wird.
Mohammed Noureldin

38

Vergleich zweier Optionen:


Überspringen (zufällige Anzahl von Zeilen)

Methode

private T getRandomEntity<T>(IGenericRepository<T> repo) where T : EntityWithPk<Guid> {
    var skip = (int)(rand.NextDouble() * repo.Items.Count());
    return repo.Items.OrderBy(o => o.ID).Skip(skip).Take(1).First();
}
  • Nimmt 2 Abfragen

Generiertes SQL

SELECT [GroupBy1].[A1] AS [C1]
FROM   (SELECT COUNT(1) AS [A1]
        FROM   [dbo].[People] AS [Extent1]) AS [GroupBy1];

SELECT TOP (1) [Extent1].[ID]            AS [ID],
               [Extent1].[Name]          AS [Name],
               [Extent1].[Age]           AS [Age],
               [Extent1].[FavoriteColor] AS [FavoriteColor]
FROM   (SELECT [Extent1].[ID]                                  AS [ID],
               [Extent1].[Name]                                AS [Name],
               [Extent1].[Age]                                 AS [Age],
               [Extent1].[FavoriteColor]                       AS [FavoriteColor],
               row_number() OVER (ORDER BY [Extent1].[ID] ASC) AS [row_number]
        FROM   [dbo].[People] AS [Extent1]) AS [Extent1]
WHERE  [Extent1].[row_number] > 15
ORDER  BY [Extent1].[ID] ASC;

Guid

Methode

private T getRandomEntityInPlace<T>(IGenericRepository<T> repo) {
    return repo.Items.OrderBy(o => Guid.NewGuid()).First();
}

Generiertes SQL

SELECT TOP (1) [Project1].[ID]            AS [ID],
               [Project1].[Name]          AS [Name],
               [Project1].[Age]           AS [Age],
               [Project1].[FavoriteColor] AS [FavoriteColor]
FROM   (SELECT NEWID()                   AS [C1],
               [Extent1].[ID]            AS [ID],
               [Extent1].[Name]          AS [Name],
               [Extent1].[Age]           AS [Age],
               [Extent1].[FavoriteColor] AS [FavoriteColor]
        FROM   [dbo].[People] AS [Extent1]) AS [Project1]
ORDER  BY [Project1].[C1] ASC

1
Vielen Dank für den Vergleich, es hilft wirklich
Haobo

Dies ist die richtige Antwort. Die markierte Antwort wird nicht empfohlen, da dies zu Leistungsproblemen führen kann.
Jacob

1
Die Frage lautet "Zeilen" im Plural. Wie würden Sie Ihre Lösung darauf anwenden? Mir scheint, ich muss dasselbe SQL mehrmals ausführen, da OrderBy(o => o.ID).Skip(skip).Take(5)es nicht wirklich zufällig ist, was zu einem Engpass in der Leistung führen kann.
Mike Mat

@ MikeMat Entfernen Sie einfach das " .First()". Ich habe einen Vergleich mit einigen anderen Antworten vorgelegt, die ich gesehen habe und die nicht mehr erscheinen, sodass Ihr Punkt doppelt bestätigt wird. Die NewGuidLösung hat jedoch nicht das von Ihnen beschriebene Problem.
Drzaus
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.