首页 > 解决方案 > 使用 Distinct 和 Order By 时实体框架中的多个查询和较差的性能

问题描述

EF 中的以下 LINQ 查询(EFCore,v2.2.1)

    var x = context.Exchange 
            .Include(q => q.Input)
            .Where(q => q.InputId !=  1&& 
                q.Input.CreatedOnUtc > DateTime.Parse("2019-11-25") && 
                q.Input.UserId == 2 && 
                q.BotConversationId == 3)
            .Distinct()
            .OrderBy(q => q.Input.CreatedOnUtc)
            .FirstOrDefault()

最终给出分析的 SQL 结果(简化)

select * from (
    select distinct e.* 
    from Exchange e, ExchangeInput i 
    where e.InputId = i.InputId
    and e.InputId <> 1
    and i.UserId = 2
    and e.BotConversationId = 3
)

select * from ExchangeInput

为什么需要做两个单独的查询?当 ExchangeInput 可能有数百万行时,第二个查询是可怕的。当然,这就足够了:

select * from (
    select distinct e.*, i.CreatedOnUtc 
    from Exchange e, ExchangeInput i 
    where e.InputId = i.InputId
    and e.InputId <> 1
    and i.UserId = 2
    and e.BotConversationId = 3
) a
order by a.CreatedOnUtc 

另外 - 正如我所料,将 Distinct 放在 order by 之后只给出 1 个查询。

解决问题很容易。.Select(...)在 the 之前添加 a.Distinct或删除 the.Distinct()即可。但最初的、性能不佳的代码在审查时似乎并没有立即出现问题。

标签: c#sql-serverentity-framework

解决方案


我首先建议Distinct()在 a 之前调用FirstOrDefault()是不必要的。当您有 OrderBy 时,“非独特”查询中的第一行应始终与“独特”查询相同!正如您在上一句中提到的,似乎删除Distinct()应该只创建一个查询。

除了您的问题,我还建议DateTime.Parse("2019-11-25")在查询之外进行计算。这应该允许您将它作为参数传递给数据库服务器,这可能会使您的查询更加高效。

总而言之,我会尝试:

var dateFilter = DateTime.Parse("2019-11-25");
var x = context.Exchange 
        .Include(q => q.Input)
        .Where(q => q.InputId != 1 && 
            q.Input.CreatedOnUtc > dateFilter && 
            q.Input.UserId == 2 && 
            q.BotConversationId == 3)
        .OrderBy(q => q.Input.CreatedOnUtc)
        .FirstOrDefault()

推荐阅读