我有一个简单的实体,我的SQL Sever 2012数据库中有100,000个实体:
public class Entity
{
public int Id { get; set; }
public string Field1 { get; set; }
public string Field2 { get; set; }
}
我想在网格中显示这些内容,分页为100,000,太多而无法在一个屏幕上显示(并且效率不高)。网格应该允许排序和过滤 - 显然所有这些操作中的3个最好在服务器上完成,EF应该翻译它们。
所以,让我们得到按Field1排序的第二页500:
var items = context.Entities.OrderBy(e => e.Field1).Skip(500).Take(500);
执行此查询时, 12秒!所以我挖了它,发现它的翻译如下:
SELECT TOP (500) [Extent1].[Id] AS [Id],
[Extent1].[Field1] AS [Field1],
[Extent1].[Field2] AS [Field2]
FROM (SELECT [Extent1].[Id] AS [Id],
[Extent1].[Field1] AS [Field1],
[Extent1].[Field2] AS [Field2],
row_number() OVER (ORDER BY [Extent1].[Field1] ASC) AS [row_number]
FROM [dbo].[Costs] AS [Extent1]) AS [Extent1]
WHERE [Extent1].[row_number] > 500
ORDER BY [Extent1].[Field1] ASC
当然这是两次排序?我没有SQL专家,但是子查询按Field1排序并将此顺序分配给row_number。然后我们将TOP 500的row_numbers超过500以获得最多500行的第2页。我们不需要再次按Field1订购结果。
如果我拿出最后的ORDER BY [Extent1].[Field1] ASC
,查询结果似乎相同,执行时间下降到大约150毫秒。
所以,显然150毫秒优于12秒 - 有什么我做错了吗?我能做些什么来解决这个问题吗?
更新
两者的查询计划相同。排序工具提示的唯一区别是“实际行数”和#39; 12s查询为4,604,150ms查询为1,134。我想补充一点,这是从15个单词的固定列表中生成的数据(对于此测试) - 即Field1包含15个值中的1个,因此基本上有15个组,每组6,666行。
答案 0 :(得分:6)
这是由于当TOP和Gather Streams组合在一起时SQL Server中的错误/特性。 索引将修复它,同样会禁用并行性(全局,或此用户或查询)。线索是Gather Streams溢出到tempdb,这是一种非常罕见的情况。 http://web.archive.org/web/20180220120719/http://sqlblog.com:80/blogs/paul_white/archive/2012/05/03/parallel-row-goals-gone-rogue.aspx这是500级别的东西。
请注意,您不能忽略最终ORDER BY
,因为这会导致结果的顺序未定义。
答案 1 :(得分:1)
我认为您的问题是因为在大型未编入索引列上执行了订单。
请确保您为该列编制索引。
无论如何,order by
子句会更好,而不是nvarchar
值