Código EF Primero: Cómo obtener filas aleatorias

82

¿Cómo puedo crear una consulta en la que recuperaría filas aleatorias?

Si tuviera que escribirlo en SQL, pondría un pedido en newid () y cortaría n número de filas desde la parte superior. De todos modos, ¿hacer esto en el código EF primero?

Intenté crear una consulta que usa newid () y ejecutarla usando DbSet.SqlQuery (). mientras funciona, no es la solución más limpia.

Además, intente recuperar todas las filas y ordenarlas por una nueva guía. Aunque el número de filas es bastante pequeño, todavía no es una buena solución.

¿Algunas ideas?

Mel
fuente

Respuestas:

164

Solo llama:

something.OrderBy(r => Guid.NewGuid()).Take(5)
SLaks
fuente
hola, funciona bien, pero será rápido cuando la tabla tenga más filas,
publiqué una
3
Vea esta pregunta , desafortunadamente está roto. Parece que OrderByasume que la función de clasificación es estable, lo que no es el caso con un generador aleatorio. Linq a entidades traduce esto a una consulta sql que puede obtener una clasificación diferente para la misma entidad (tan pronto como se utilicen sus consultas Include). Luego, hace que la entidad se duplique en la lista de resultados.
Frédéric
1
No estoy seguro de si confiaría en esto para las tareas que requieren un conjunto de filas aleatorias acorazado; probablemente iría con stackoverflow.com/a/654910/12484 o stackoverflow.com/a/648247/12484 en su lugar, pero este enfoque simple funcionó bien para mi necesidad, que requería una sola fila pseudoaleatoria para una función no orientada al cliente. +1.
Jon Schneider
@Toolkit probablemente no sea tan extraño, si Entity no tiene un equivalente de Oracle Guid.NewGuid()(es decir, LinqToSql o lo que sea que lo convierta, NEWID()pero nadie programó lo mismo para Oracle).
drzaus
¿Es este enfoque eficaz? En otra parte, encontré que este método, por alguna consideración de rendimiento, no se recomienda.
Mohammed Noureldin
40

Comparando dos opciones:


Saltar (número aleatorio de filas)

Método

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();
}
  • Toma 2 consultas

SQL generado

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

Método

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

SQL generado

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
drzaus
fuente
1
Gracias por comparar, realmente ayuda
Haobo
Esta es la respuesta correcta. No se recomienda la respuesta marcada, ya que podría causar algunos problemas de rendimiento.
Jacob
1
La pregunta dice "filas" en plural, ¿cómo aplicaría su solución a eso? A mí me parece que tendré que ejecutar el mismo SQL varias veces, porque OrderBy(o => o.ID).Skip(skip).Take(5)no será realmente aleatorio, lo que podría convertirse en un cuello de botella en el rendimiento.
Mike Mat
@MikeMat Simplemente elimine el " .First()". Estaba presentando una comparación entre algunas otras respuestas que había visto y que ya no aparecen, por lo que su punto está doblemente validado. Pero la NewGuidsolución no tendrá el problema que describe.
drzaus