EF Code First:如何获取随机行

97

如何构建一个查询以检索随机行?

如果我要用SQL编写它,那么我会在newid()上放置一个order by,并从顶部切掉n个行。是否有办法在EF代码优先中实现这一点?

我尝试创建一个使用newid()的查询,并使用DbSet.SqlQuery()执行它。虽然它有效,但不是最简洁的解决方案。

另外,尝试检索所有行并按新的guid排序。虽然行数相当小,但仍不是一个好的解决方案。

有什么想法吗?


2
请参见以下链接:https://dev59.com/MnRB5IYBdhLWcg3wXWK2#3345272 - Ian Mercer
可能是[Linq to Entities, random order]的重复问题(https://dev59.com/7XRB5IYBdhLWcg3wV156)。 - Frédéric
4个回答

193

只需调用:

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

嗨,它运行得很好,但是当表格有更多行时,这个速度会快吗?我在这里发布了一个问题。 - Shaiju T
3
请参见此问题,不幸的是已经出现了问题。似乎OrderBy假定排名函数是稳定的,但在使用随机生成器时并非如此。Linq to Entities会将其转换为SQL查询,可能会为同一实体获得不同的排名(只要您的查询使用了Include)。这会导致实体在结果列表中重复出现。 - Frédéric
1
我不确定是否能够相信这个解决方案来处理需要一个严格的随机行数的任务 - 我可能会选择使用https://dev59.com/7XRB5IYBdhLWcg3wV156#654910或者https://dev59.com/MnRB5IYBdhLWcg3wXWK2#648247,但是对于我的需求,这种简单的方法完全可以胜任,因为我只需要一个伪随机的行用于一个非面向客户的特性。点赞。 - Jon Schneider
@Toolkit可能并不陌生,如果Entity没有Guid.NewGuid()的Oracle等效项(意思是,LinqToSql或其他东西将其转换为NEWID(),但没有人为Oracle编写相同的代码)。 - drzaus
2
自从 .NET 5 版本以后,使用 includes 时会导致奇怪的行为。 - Enes Sadık Özbek
显示剩余2条评论

49

比较两个选项:


跳过(随机行数)

方法

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;

Guid

方法

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

2
谢谢你的比较,真的很有帮助。 - Haobo
这是正确的答案,不推荐标记的答案,因为它可能会导致一些性能问题。 - Jacob
1
问题中提到“行”是复数形式,您如何将解决方案应用于此?对我来说,似乎我必须多次执行相同的SQL,因为OrderBy(o => o.ID).Skip(skip).Take(5)不会真正随机,这可能会成为性能瓶颈。 - Mike Mat
@MikeMat 只需删除“.First()”即可。我展示了一些其他答案的比较,但它们似乎不再存在,所以你的观点得到了双重验证。但是NewGuid解决方案不会出现你描述的问题。 - drzaus

27

EF Core 6 中新增了一个函数:EF.Functions.Random()

something.OrderBy(r => EF.Functions.Random()).Take(5)

1
注意:在SQL Server中,此函数使用RAND()函数,该函数对所有行只执行一次 - 这不会是随机的。请参见:https://dba.stackexchange.com/a/974/245938 接受的解决方案更好。 - Mr Patience
1
@MrPatience 对我来说,它返回随机排序的随机行。 - VladL
好吧,那不是我的经历,但我有一个带有group by子句的复杂查询 - 也许是那里出了问题。 - Mr Patience

0

你可以尝试以下方法:

 public static String UdfGetRandomText()
        {
            using (Models.DbContextModel db = new Models.DbContextModel())
            {
                try
                {
                    Entity.tblRandomTexts t = new Entity.tblRandomTexts();
                    t = db.tblRandomTexts.OrderBy(r => Guid.NewGuid()).First();
                    return (t.TextBuddy + Environment.NewLine + t.TextWriter);
                }
                catch (Exception ee)
                {
                    return ee.Message;
                }
            }
        }

假设您在EF中有一个类,如下所示,它创建了表。

public partial class tblRandomTexts
{
    [Key]
    public long TextRowID { get; set; }
    [MaxLength(1500)]
    public String TextBuddy { get; set; }
    [MaxLength(100)]
    public String TextWriter { get; set; }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接