我注意到.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);
实体框架将为两个查询生成以下相同的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中的一个错误?
答案 0 :(得分:4)
在您提到的特定情况下,您提供的两个LINQ查询在功能上是等效的,因为无论Take
和Select
的顺序如何,结果集将始终相同。
至于性能问题,两个查询都将由数据库平台进行优化。我会非常惊讶地发现两者之间存在显着差异。我不希望第二个查询,例如,对它知道不会超过TOP
的一大堆项目执行投影。 LINQ查询提供程序倾向通常不关注优化,因为当SQL被转换为实际可执行代码时,该步骤往往发生在数据库级别。如今,数据库花费了大量精力来优化SQL代码的编译,因此查询提供者根本不需要复制这些工作。
但是,当您说过滤或排序时,它会更改查询实际返回的内容。
query.Take(10).Where(someFilter);
不一定(必然)返回相同的内容:
query.Where(someFilter).Take(10);
第一项需要10项并且返回但是其中许多项目都通过了过滤器。
第二个查询最多返回10个全部通过过滤器的项目。
对于您显示的两个SQL查询,它们在功能上是不同的,因为一个是在em表中和中的每一个项目然后 em>抓取20个项目,另一个抓住前20个项目,然后订购它们,这比操作更快 。
在 案例中,将Take
的语义顺序正确地转换为SQL非常重要。如果查询提供者可以证明两个给定操作的顺序并不重要,那么根据需要重新排序它们不是问题。
答案 1 :(得分:1)
答案其实非常简单。你基本上是在比较这两个问题:
select top 20 ItemId
from Items
order by ItemId desc
为:
select ItemId
from (
select top 20 ItemId
from Items
) p
order by ItemId descending
后者将随机挑选20行,而不是最高ItemIds
的20行。 那是你期望但没有获得的查询更快的原因:它不正确并且更容易由数据库运行。