我注意到 .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/