使用 EF Core 根据记录数据过滤日期范围内的数据总是超时。我是否做了什么导致表现如此糟糕的事情?

问题描述 投票:0回答:1

我有一个测试,随着时间的推移会有很多记录。我正在使用 Postgresql 来存储这些测试和记录,并且我希望能够搜索具有特定时间范围内的记录的测试。

一个测试有很多记录,每条记录都有一个

collectionTime
值,该值存储为
DateTime
。当我搜索所有测试时,我使用 EF Core 和 Linq 使用如下查询:

var tests = Table.Include(t => t.RecordData)
                 .AsSplitQuery().AsNoTracking()
                 .Select(r => new Test 
                              { 
                                  Id = r.Id,
                                  // ...additional test info here...
                                  FirstReported = r.RecordData.OrderBy(rd => rd.CollectionDate).FirstOrDefault().CollectionDate,
                                  LastReported = r.RecordData.OrderByDescending(rd => rd.CollectionDate).FirstOrDefault().CollectionDate,
                                  NumberOfRecords = r.RecordData.Count,
                              });

此时,我开始执行不同的过滤和排序。这会根据用户应用的过滤器修改结果查询。如果用户只是获取所有记录,然后对它们进行分页并返回而不进行过滤,那么查询就没有问题。但是,当我添加逻辑来检查是否提供了特定时间段时,我的查询始终超时。

if (providedStartDate is not null)
{
    var startDateUTC = providedStartDate.ToUniversalTime().Date;
    tests = tests.Where(x => x.NumberOfRecords > 0 && x.LastReported >= startDateUTC);
}

我有一个几乎相同的条件来检查

FirstReported
收集时间是否在
providedEndDate
之后,因为像这样过滤掉两侧在逻辑上会产生我想要的测试。

对结果进行分页后,我将它们发送到列表中:

return await tests.ToListAsync(token);

我同样想计算用于分页目的的记录数,并且在过滤日期范围内的测试时

CountAsync
也会停止。

对于查询和逻辑为何此查询执行如此差/超时有任何建议或直接关注吗?我认为这是相当常见的事情,但我似乎找不到任何解决此类优化搜索的具体帖子。谢谢。

排除附加信息后,为

StartDate
生成的原始 SQL 如下。请注意,我有一个查询过滤器,仅获取活动记录和测试,但忽略查询过滤器也不会以任何显着的方式影响性能:

SELECT 
    t."Id", 
    (SELECT t2."CollectionDate"
     FROM public."RecordData" AS t2
     WHERE t2."IsActive" = TRUE AND t."Id" = t2."TestId"
     ORDER BY t2."CollectionDate"
     LIMIT 1) AS "FirstReported", 
    (SELECT t3."CollectionDate"
     FROM public."RecordData" AS t3
     WHERE t3."IsActive" = TRUE AND t."Id" = t3."TestId"
     ORDER BY t3."CollectionDate" DESC
     LIMIT 1) AS "LastReported", 
    (SELECT count(*)::int
     FROM public."RecordData" AS t4
     WHERE t4."IsActive" = TRUE AND t."Id" = t4."TestId") AS "NumberOfRecords"
FROM 
    public."Tests" AS t
WHERE 
    t."IsActive" = TRUE 
    AND (SELECT count(*)::int
         FROM public."RecordData" AS t0
         WHERE t0."IsActive" = TRUE AND t."Id" = t0."TestId") > 0 
    AND (SELECT t1."CollectionDate"
         FROM public."RecordData" AS t1
         WHERE t1."IsActive" = TRUE AND t."Id" = t1."TestId"
         ORDER BY t1."CollectionDate" DESC
         LIMIT 1) >= @__startDateUTC_0
LIMIT @__p_2 OFFSET @__p_1
c# asp.net postgresql linq entity-framework-core
1个回答
0
投票

看起来 EF Core LINQ 翻译不好,请尝试以下方法

var tests = 
    from t in Table
    from first in r.RecordData
        .OrderBy(rd => rd.CollectionDate)
        .Take(1).DefautIfEmpty()
    from last in r.RecordData
        .OrderByDescending(rd => rd.CollectionDate)
        .Take(1).DefautIfEmpty()
    select new Test
    {
        Id = r.Id,
        ...additional test info here...
        FirstReported = first.CollectionDate,
        LastReported = last.CollectionDate,
        NumberOfRecords = r.RecordData.Count(),
    };
© www.soinside.com 2019 - 2024. All rights reserved.