我只是好奇Skip和Take函数如何在Entity Framework中工作(使用EF 6.1)。
如果我这样做:
db.Events.OrderByDescending(x => x.Date).Take(maxPageSize).ToList();
我得到一些清单(注意到一个事件已经完全消失)。
如果我这样做:
db.Events.OrderByDescending(x => x.Date).Skip(0).Take(maxPageSize).ToList();
我得到另一个列表,之前的查询中出现的事件就在这里。
任何人都知道为什么我必须Skip()
ZERO 实体才能Take()
我应该采取的措施?这几乎没有任何意义(至少对我而言)......
P.S。我无法使用SQL Server Profiler来检查生成的查询。
答案 0 :(得分:2)
生成了完全不同的查询。在第一种情况下,您只需前N行:
SELECT TOP(@maxPageSize) ...
FROM [Events]
ORDER BY [Date] DESC
在第二种情况下,行号用于过滤有序行:
SELECT ...
FROM (SELECT ROW_NUMBER() OVER (ORDER BY [Date] DESC) AS [ROW_NUMBER], ...
FROM [Events])
WHERE [ROW_NUMBER] BETWEEN @skip + 1 AND @skip + @maxPageSize
ORDER BY [ROW_NUMBER]
ROW_NUMBER()返回从1开始的行号。如果页面大小为5,则第一个查询将按预期返回前5行。第二个查询将返回数字为1,2,3,4,5的行。因此,当@skip参数等于零时,这两个查询之间应该没有任何区别。
确保您确实执行具有完全相同参数的精确指定的查询。运行这些查询时,确保事件表具有完全相同的数据。检查您是否使用最新版本的组件。
答案 1 :(得分:2)
尽管细节可能有所不同,但在描述发出的查询有何不同时,公认的答案最正确。我通常看到实体框架以“ TOP(@maxPageSize).... WHERE [ROW_NUMBER]> @skip”的形式发出查询,这可能是相同的区别,但是我不确定它会生成相同的查询执行吗?计划。
要清楚注意的重要区别是,当您的“ ORDER BY”不包含唯一的列名时,这可能会产生不同的结果。
在我编写的代码中,当@skip值为0时,我们省略了“跳过”。最后一个条目出现了一个条目。如果然后转到应用了@skip值的下一页,则该条目将作为该页的第一项出现。应该至少处于那些位置之一的“捆绑”物品从未出现过。这是每个发出的SQL:
不跳过(在第1页上生成):
SELECT TOP ({take number}) [Extent1].[Table1ID] AS [Table1ID], {snip a lot of other columns}
FROM [dbo].[Table1] AS [Extent1]
LEFT OUTER JOIN [dbo].[Table2] AS [Extent2] ON [Extent1].[Table2ID] = [Extent2].[Table2ID]
WHERE ({my where clause conditions})
ORDER BY [Extent2].[MySortColumn] ASC
跳过:
SELECT TOP ({take number}) [Filter1].[Table1ID], {snip a lot of other columns}
FROM ( SELECT [Extent1].[Table1ID] AS [Table1ID], {snip a lot of other columns}, row_number() OVER (ORDER BY [Extent2].[MySortColumn] ASC) AS [row_number]
FROM [dbo].[Table1] AS [Extent1]
LEFT OUTER JOIN [dbo].[Table2] AS [Extent2] ON [Extent1].[Table2ID] = [Extent2].[Table2ID]
WHERE ({my where clause conditions})
) AS [Filter1]
WHERE [Filter1].[row_number] > {skip number}
ORDER BY [Filter1].[MySortColumn] ASC
重要的收获是,一路回到“ SQL 101”并记住without "order by", order is not guaranteed。这种发光方式的差异导致一个项目在网格的多个页面上重复出现,而另一个项目却从未出现过,这使我看到,这在实体框架中更为重要,因为在实体框架中生成的确切SQL并不直接在您的控件中显示, 在将来的EF版本中可能会意外地有所不同。
当前,您可以通过始终包含Skip(0)来避免此情况,后者将发出第二个查询,但{skip number}只是零。在我的测试中,这似乎总是使用T-SQL的默认规则为平局决胜局发出相同的顺序。我认为,并不是最好的做法是假设这在将来的Entity Framework这样的版本中一定会起作用。我建议您考虑自己探索tie-breaking strategy。就我而言,应该有可能打破“ Table1ID”上的关系,因为它代表唯一的身份主键列。附件中提供了针对更复杂情况的建议。