【问题标题】:Linq Query slow with Count. SQL query is very fast. What am I doing wrong?Linq Query 计数缓慢。 SQL查询非常快。我究竟做错了什么?
【发布时间】:2021-07-17 21:11:59
【问题描述】:

我有一个非常基本的父子关系。对于我的主页,我只想从 children 表中获取计数。

var assignmenTotal = new AssignmentUser
{
    IsSupervisor = supervisor,
    AssignmentTotals = (
        from a in db.Assignments
        where (StartDate.HasValue) 
            ? DbFunctions.TruncateTime(a.CreatedDate) == StartDate 
            : a.IsArchived == false
        orderby a.ID ascending
        join b in db.Adjustments on a.ID equals b.AssignmentID
        group b by new {a.ID,a.UserName,a.Status,a.CreatedDate,a.IsArchived} 
        into g
        select new AssignmentTotals
        {
            ID =  g.Key.ID,
            UserName = g.Key.UserName,
            Status = g.Key.Status,
            ImportedDate = DbFunctions.TruncateTime(g.Key.CreatedDate),
            StartingLocation = (db.Adjustments
                .Where(x => x.AssignmentID == g.Key.ID)
                .OrderBy(x => x.LocationID)
                .Select(x => x.LocationID)
                .FirstOrDefault()),
            EndingLocation = (db.Adjustments.
                Where(x => x.AssignmentID == g.Key.ID)
                .OrderByDescending(x => x.LocationID)
                .Select(x => x.LocationID)
                .FirstOrDefault()),
            TotalLocations = g.Count(x => x.LocationID != null),
            TotalLicensePlates = g.Count(x => x.ExpectedLicensePlateID != null),
            TotalAdjCompleted = g.Count(x => x.Status == "C"),
            IsSameUser = (currUser == g.Key.UserName ? true : false),
            IsArchived = g.Key.IsArchived
        })
        .OrderBy(x => x.ID)
        .ToList()
};

现在展平的行总数约为 1000,这大约需要 10 秒才能完成。 如果我写一个 SQL 查询

SELECT ID, UserName, Status, b.StartLocation, b.EndLocation, b.TotalLocations, 
       b.TotalLicensePlates, b.TotalLocations 
FROM Assignments a
INNER JOIN(
SELECT AssignmentID,
min(LocationID) as StartLocation, max(LocationID) as EndLocation,
COUNT(CASE WHEN LocationID is NOT NULL THEN 1 ELSE 0 end) AS TotalLocations,
SUM(CASE WHEN ExpectedLicensePlateID IS NOT NULL THEN 1 ELSE 0 END )TotalLicensePlates,
SUM(CASE WHEN Status = 'C' THEN 1 ELSE 0 END )TotalAdjCompleted
FROM dbo.Adjustments
group by AssignmentID
) b on (a.ID = b.AssignmentID)
WHERE convert(date,a.CreatedDate) ='04/23/2021'

这需要不到一秒钟的时间来完成。

我认为我的问题出在 linq COUNT 部分。我试过做一个子查询,但仍然很慢。我认为问题在于 linq 查询将所有数据带到客户端并在客户端完成所有工作,而不是让服务器完成所有工作?

有没有更好的方法来做到这一点?

编辑:我正在使用实体框架,当我检查 SQL 分析器时,SQL 发送非常长且复杂。

【问题讨论】:

  • 你在使用实体框架吗?很可能您非常复杂的 LINQ 查询无法转换为单个 SQL 语句,因此它会执行许多昂贵的查询并在内存中执行其余操作。您是否对通信进行了 SQL 配置文件来验证这一点?
  • 您好,是的。我正在使用 EF 并检查了 SQL Profiler 并进行了非常长的复杂 SQL 查询。我有什么选择?
  • 为什么没有发布EF生成的SQL?
  • 我会尽量简化查询。只需选择(也许只是必填字段)父母和他们的孩子。我认为,如果 EF 在性能方面存在问题,那么在此之后执行 .AsEnumerable 并在 C# 中继续进行计数和空值检查会更快。

标签: c# sql linq


【解决方案1】:

这里的问题是,您编写了非等效的 LiNQ 查询。在投影中没有最佳预测如何处理FirstOrDefault。它会创建额外的 OUTER APPLY 连接,但速度很慢。

重写您的查询以尽可能接近 SQL:

 var query =
    from a in db.Assignments
    where (StartDate.HasValue) 
        ? DbFunctions.TruncateTime(a.CreatedDate) == StartDate 
        : a.IsArchived == false
    orderby a.ID ascending
    join b in db.Adjustments on a.ID equals b.AssignmentID
    group b by new {a.ID,a.UserName,a.Status,a.CreatedDate,a.IsArchived} 
    into g
    select new AssignmentTotals
    {
        ID =  g.Key.ID,
        UserName = g.Key.UserName,
        Status = g.Key.Status,
        ImportedDate = DbFunctions.TruncateTime(g.Key.CreatedDate),
        StartingLocation = g.Min(x => x.LocationID)
        EndingLocation = g.Max(x => x.LocationID),
        TotalLocations = g.Count(x => x.LocationID != null),
        TotalLicensePlates = g.Count(x => x.ExpectedLicensePlateID != null),
        TotalAdjCompleted = g.Count(x => x.Status == "C"),
        IsSameUser = (currUser == g.Key.UserName ? true : false),
        IsArchived = g.Key.IsArchived
    };

var totals = query
        .OrderBy(x => x.ID)
        .ToList();

var assignmenTotal = new AssignmentUser
{
    IsSupervisor = supervisor,
    AssignmentTotals = totals
};

【讨论】:

    【解决方案2】:

    实体框架(就像任何其他编程对象关系映射器一样)的性能和效率越差,您的请求越复杂。

    所以,选项:

    1. 处理它。嗯,这不是最好的。显然它会很慢,但如果它有效,它就会有效。
    2. 在 EF EF6 / core 中使用原始 SQL 查询。
    3. 使用不同的 ORM,但您需要自己评估它们以找出优缺点。
    4. 完全放弃 C# 中的 SQL,并使用存储过程、视图、函数和其他 SQL 对象将 SQL 查询保留在数据库中。 (感谢Antonín Lejsek

    【讨论】:

    • 最终做了一个原始的 sql
    猜你喜欢
    • 1970-01-01
    • 2013-08-06
    • 1970-01-01
    • 1970-01-01
    • 2021-07-27
    • 2016-07-18
    • 1970-01-01
    • 2021-04-13
    • 2019-12-23
    相关资源
    最近更新 更多