c# - .Take() 的位置对生成的 SQL 没有影响

标签 c# .net linq entity-framework

我注意到 .Take() 的位置对生成的 SQL 没有影响。例如说我有这个查询:

IQueryable<Item> query = 
    db.Items.Where(i => i.CategoryID == categoryID && i.Deleted == false)
            .OrderByDescending(i => i.ItemID).Skip(0);

然后做下面两个查询:

IQueryable<ItemViewModel> query1 = 
    query.Take(20).Select(i => new ItemViewModel { ItemID = i.ItemID });
IQueryable<ItemViewModel> query2 = 
    query.Select(i => new ItemViewModel { ItemID = i.ItemID }).Take(20);

Entity Framework 将为两个查询生成以下相同的 SQL:

SELECT TOP (20) 
  [Project1].[ItemID] AS [ItemID], 
FROM ( SELECT [Project1].[ItemID] AS [ItemID],
   row_number() OVER (ORDER BY [Project1].[ItemID] DESC) AS [row_number]
    FROM ( SELECT 
        [Filter1].[ItemID] AS [ItemID], 
        FROM   (SELECT [Extent1].[ItemID] AS [ItemID], 
    [Extent1].[CategoryID] AS [CategoryID]
    FROM  [dbo].[Items] AS [Extent1]
            WHERE 0 = [Extent1].[Deleted] ) AS [Filter1]
        WHERE ([Filter1].[CategoryID] = @p__linq__0) AND (@p__linq__0 IS NOT NULL)
    )  AS [Project1]
)  AS [Project1]
WHERE [Project1].[row_number] > 0
ORDER BY [Project1].[ItemID] DESC

为什么要这样做?它不应该为子查询上的 TOP 20 为 query1 生成以下内容吗?

SELECT 
    [Project1].[ItemID] AS [ItemID]
    FROM ( SELECT [Project1].[ItemID] AS [ItemID]
        FROM ( SELECT TOP (20)
            [Extent1].[ItemID] AS [ItemID], 
            row_number() OVER (ORDER BY [Extent1].[ItemID] DESC) AS [row_number]
            FROM [dbo].[Items] AS [Extent1]
            WHERE ([Extent1].[CategoryID] = @p__linq__0) AND (@p__linq__0 IS NOT NULL)  
            AND (0 = [Extent1].[Deleted]) AND [Extent1].[row_number] > 0
            ORDER BY [Extent1].[ItemID] DESC
        )  AS [Project1]
    )  AS [Project1]

我注意到将 TOP 20 移入内部会增加我实际查询中的 7 秒到立即响应的时间,因为它不会在 TOP 20 之前进行投影.

编辑:不幸的是,自从 query.Take(20).Select(i => new { ... }).ToString() = = query.Select(i => new { .. }).Take(20).ToString().也许这是 EF 中的错误?

最佳答案

在您提到的特定情况下,您提供的两个 LINQ 查询在功能上是等效的,因为无论 Take 的顺序如何,结果集将始终相同选择

至于性能问题,这两个查询都将由数据库平台进行优化。看到两者之间有任何显着差异,我会感到非常惊讶。例如,我不希望第二个查询对它知道不会超过 TOP 的一大堆项目执行投影。通常,LINQ 查询提供程序倾向于不关注优化,这仅仅是因为当 SQL 被翻译成实际的可执行代码时,该步骤往往发生在数据库级别。如今,数据库花费了大量精力来优化 SQL 代码的编译,因此查询提供者根本不需要重复这些工作。

但是,当您说过滤或排序时,它会改变查询实际返回的内容。

query.Take(10).Where(someFilter);

不会(必然)返回与以下内容相同的内容:

query.Where(someFilter).Take(10);

第一个接受 10 个项目并返回,但是其中许多项目通过了过滤器。

第二个查询返回最多 10 个全部通过过滤器的项目。

就您展示的两个 SQL 查询而言,它们在功能上是不同的,因为一个查询while 表中的每个项目然后 em> 抓取 20 件元素,另一个是抓取前 20 件元素然后对它们进行排序,这比操作快得多。

那种情况下,将 Take 的语义顺序正确转换为 SQL 非常重要。如果查询提供程序可以证明两个给定操作的顺序并不重要,那么它可以根据需要重新排序它们。

关于c# - .Take() 的位置对生成的 SQL 没有影响,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22104930/

相关文章:

c# - 使用 Linq C# 对多级 XML 进行排序

c# - 从 lambda 表达式生成 sql server 查询的替代方法

c# - 从 C# 执行 powershell 命令

c# - 在 MVC Action 中使用基类作为重载,但收集所有数据

c# - 是否存在将我的泛型方法限制为数字类型的约束?

c# - 限制插件汇编代码访问

c# - 在单声道中加密 web.config 的配置部分

c# - 日期时间 AM/PM 格式转换为 24 小时格式

c# - 如何使用 LINQ 检索 SharePoint 用户数据?

C# HashSet<string> 到单个字符串