EF Code First: как получить случайные строки

82

Как я могу создать запрос, в котором я получу случайные строки?

Если бы я написал это на SQL, я бы поставил заказ на newid () и отрубил бы n строк сверху. В любом случае, сначала сделать это в коде EF?

Я попытался создать запрос, использующий newid (), и выполнить его с помощью DbSet.SqlQuery (). пока он работает, это не самое чистое решение.

Кроме того, попытался получить все строки и отсортировать их по новому идентификатору. Хотя количество строк довольно мало, это все же не лучшее решение.

Есть идеи?

Мел
источник

Ответы:

164

Просто позвони:

something.OrderBy(r => Guid.NewGuid()).Take(5)
SLaks
источник
привет, он работает нормально, но будет ли это быстро, когда в таблице будет больше строк, я разместил вопрос здесь
Shaiju T
3
См. Этот вопрос , к сожалению, он сломан. Похоже, OrderByпредполагается, что функция ранжирования стабильна, чего нельзя сказать о случайном генераторе. Linq to entity преобразует это в sql-запрос, который может получить разный рейтинг для одного и того же объекта (как только ваши запросы будут использовать Include). Затем это приводит к дублированию объекта в списке результатов.
Frédéric
1
Не уверен, что я доверяю этому для задач, требующих надежного набора случайных строк - я бы, вероятно, выбрал stackoverflow.com/a/654910/12484 или stackoverflow.com/a/648247/12484 вместо этого - но этот простой подход отлично работал для моих нужд, которые требовали единственной псевдослучайной строки для функции, не ориентированной на клиента. +1.
Джон Шнайдер
@Toolkit, вероятно, не так уж и странен, если Entity не имеет эквивалента Oracle Guid.NewGuid()(имеется в виду LinqToSql или что-то еще, NEWID()но никто не программировал то же самое для Oracle).
drzaus
Насколько эффективен такой подход? Я где-то еще обнаружил, что этот метод - из соображений производительности - не рекомендуется.
Mohammed Noureldin
40

Сравнение двух вариантов:


Пропустить (случайное количество строк)

Метод

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();
}
  • Принимает 2 запроса

Сгенерированный 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;

Гид

Метод

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

Сгенерированный 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
Drzaus
источник
1
Спасибо за сравнение, это действительно помогает
Haobo 07
Это правильный ответ. Отмеченный ответ не рекомендуется, так как это может вызвать проблемы с производительностью.
Джейкоб
1
В вопросе указано «строки» во множественном числе, как бы вы применили к этому свое решение? Мне кажется, что мне придется выполнять один и тот же SQL несколько раз, потому что OrderBy(o => o.ID).Skip(skip).Take(5)он не будет действительно случайным, что может стать узким местом в производительности.
Майк Мэт,
@MikeMat Просто удалите " .First()". Я представлял сравнение между некоторыми другими ответами, которые я видел, которые больше не появляются, так что ваша точка зрения дважды подтверждается. Но в NewGuidрешении не будет проблемы, которую вы описываете.
drzaus