使用Distinct和Order By时,实体框架中的多个查询和性能不佳

时间:2019-11-27 01:40:36

标签: c# sql-server entity-framework

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放在订单后仅给出1个查询。

解决问题很容易。在.Select(...)之前添加.Distinct或删除.Distinct()即可。但是,最初的性能不佳的代码在查看时似乎不会立即成为问题。

1 个答案:

答案 0 :(得分:3)

我首先建议不要在Distinct()之前调用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()