LINQ to Entities - 跳过并且速度非常慢

时间:2018-03-30 09:52:58

标签: c# linq-to-entities

我正在编写一个处理大量图像的替换系统。我已经迁移了被认为是"生活"图像到新数据库,但需要通过UI搜索旧数据库,以允许用户拉出不同的图像。

我在我的数据库中放置了一个查询旧数据库的视图,以便我可以通过Entity Framework公开它。该视图效果很好,尽管有数百万条记录,但我得到的结果还不错。

我的问题是当我尝试使用LINQ to Entities运行查询时。我的搜索被分页,并将结果限制为每页100个。

我有一个IQueryable用于过滤结果,但对于Skip / Take我必须添加一个订单。下面的例子显示了获得结果的第2页(Skip 100,Take 100)。

Array
(
    [0] => https://media.lewatmana.com/cam/tbicibubur/248/snapshot20180330_163142M-thumb.jpg
)

它生成的SQL是:

        if (firstId.HasValue)
            query = query.Where(x => x.Id >= firstId.Value);

        if (!string.IsNullOrEmpty(groupCode))
            query = query.Where(x => x.GroupCode == groupCode);

        var daResults = query
            .OrderBy(x => x.Id)
            .Skip(100)
            .Take(100)
            .ToList();

问题似乎是对第一个投影进行了全面评估(大约1000万条记录),然后在返回前100行之前对其进行排序。查询执行计划将SORT成本显示为批次的92%。

这个查询大约需要30秒,只是在超时的尖端,所以它的命中和错过甚至是否返回。

我正在寻找关于如何提高速度的一些提示,针对视图的查询非常快(<1秒)。

1 个答案:

答案 0 :(得分:1)

我写完这个问题的结尾,然后采取了一种略有不同的方法。无论如何,我以为我会发帖,因为它可能有用。

我改变了我的代码结构。我现在先做一个扩展的.Take()。这使我获得了所有页面,包括我想要返回的页面。然后我按顺序跳过,只跳过我想要的页面。

SELECT 
    [Project1].[Id] AS [Id], 
    [Project1].[FolderPath] AS [FolderPath], 
    [Project1].[Filename] AS [Filename], 
    [Project1].[IsFlagged] AS [IsFlagged], 
    [Project1].[IsHidden] AS [IsHidden], 
    [Project1].[TextField1] AS [TextField1], 
    [Project1].[TextField2] AS [TextField2], 
    [Project1].[TextField3] AS [TextField3], 
    [Project1].[TextField4] AS [TextField4], 
    [Project1].[GroupCode] AS [GroupCode], 
    [Project1].[Deleted] AS [Deleted], 
    [Project1].[Created] AS [Created], 
    [Project1].[CreatedBy] AS [CreatedBy], 
    [Project1].[Updated] AS [Updated], 
    [Project1].[UpdatedBy] AS [UpdatedBy]
    FROM ( SELECT 
        [Extent1].[Id] AS [Id], 
        [Extent1].[FolderPath] AS [FolderPath], 
        [Extent1].[Filename] AS [Filename], 
        [Extent1].[IsFlagged] AS [IsFlagged], 
        [Extent1].[IsHidden] AS [IsHidden], 
        [Extent1].[TextField1] AS [TextField1], 
        [Extent1].[TextField2] AS [TextField2], 
        [Extent1].[TextField3] AS [TextField3], 
        [Extent1].[TextField4] AS [TextField4], 
        [Extent1].[GroupCode] AS [GroupCode], 
        [Extent1].[Deleted] AS [Deleted], 
        [Extent1].[Created] AS [Created], 
        [Extent1].[CreatedBy] AS [CreatedBy], 
        [Extent1].[Updated] AS [Updated], 
        [Extent1].[UpdatedBy] AS [UpdatedBy]
        FROM [dbo].[view_ProEditBulkImageInfo] AS [Extent1]
        WHERE
            ([Extent1].[Deleted] IS NULL) 
        AND ([Extent1].[Id] >= 300000)
    )  AS [Project1]
    ORDER BY row_number() OVER (ORDER BY [Project1].[Id] ASC)
    OFFSET 100 ROWS FETCH NEXT 100 ROWS ONLY

原始的Skip / Take导致以下SQL,它需要以前完全评估的内部查询,并且速度很慢:

        query = query
            .Take((page.GetValueOrDefault(0) + 1) * recordCount.GetValueOrDefault(100));

        // Now skip to the required page. 
        daResults = daResults
            .OrderBy(x => x.Id)
            .Skip(page.GetValueOrDefault(0) * recordCount.GetValueOrDefault(100))
            .ToList();

更改它,内部查询要小得多。该内部子查询使用SELECT TOP(200),它快速闪电,然后将OFFSET等应用于减少的结果

我仍然只是在所有这些事件发生之后枚举结果(.ToList()),所以这一切都停留在数据库中,结果现在又很快了。