我在EF CodeFirst中使用Take
和Skip
语句,用于实现Paging(如 Zoran Maksimovic中所述的this post),这些语句导致EF生成sql查询像这样(我的页面大小是100):
SELECT TOP (100) [Filter1].[Id] AS [Id],
[Filter1].[SendDuration] AS [SendDuration]
FROM (SELECT [Extent1].[Id] AS [Id],
[Extent1].[SendDuration] AS [SendDuration],
row_number() OVER (ORDER BY [Extent1].[SendDuration] DESC) AS [row_number]
FROM [dbo].[MyView] AS [Extent1]
WHERE (1293>= [Extent1].[Id])
)AS [Filter1]
WHERE [Filter1].[row_number] > 500
ORDER BY [Filter1].[SendDuration] DESC
但是这个sql在sql server中运行时速度很慢,但Boanerge在comment中使用row_number < X
代替Top(y)
导致性能提升。我的意思是,如果我将生成的sql更改为:
SELECT [Filter1].[Id] AS [Id],
[Filter1].[SendDuration] AS [SendDuration]
FROM (SELECT [Extent1].[Id] AS [Id],
[Extent1].[SendDuration] AS [SendDuration],
row_number() OVER (ORDER BY [Extent1].[SendDuration] DESC) AS [row_number]
FROM [dbo].[MyView] AS [Extent1]
WHERE (1293>= [Extent1].[Id])
)AS [Filter1]
WHERE [Filter1].[row_number] > 500 and [Filter1].[row_number] <= 600
ORDER BY [Filter1].[SendDuration] DESC
查询执行时间会更好,更可接受(在某些情况下,更快4或5倍)。 有没有办法强制EF生成第二个Sql而不是第一个Sql?
答案 0 :(得分:2)
如果没有看到完整的架构等,很难确定,但对我而言,这表明缺少索引 1 。
使用包含实际执行计划运行查询,并查看SSMS告诉您的内容 - 它通常会建议额外的索引。
我有一个数百万条记录的数据集 - 并且花了大约3天使用LINQpad,SSMS和执行计划的详细分析来优化每个查询。最后,我设法通过添加3个额外的索引来节省大约96%的执行时间。
1 这是一个线索,当您可以限制范围并提高性能时,索引是必需的/将有所帮助 - 因为这表明正在执行扫描而我们不喜欢扫描。
答案 1 :(得分:1)
这是算法查询生成的双重优势:查询与算法一样好。改变的唯一方法是算法决定做一些不同的事情 - 可能使用不同版本的ORM工具。当然,“不同”并不总是与“更好”相同:)
对于非平凡的查询,经验丰富的SQL开发人员将在使用正确的工具(SSMS提供您需要的大部分内容)时 - 通常能够胜过许多生成的查询。当然,经验不足的SQL开发人员最终可能会在查询错误的事情时结束。
大多数ORM提供了将原始SQL查询传递到引擎的选项 - 但这确实会失去可移植性。如果这是一个问题,另一个选择就是存储过程,其中API甚至可以在不同数据库的不同实现上相似。
您需要在此提出的问题是:
如果答案为“否”和“是”,您应该可以将原始SQL(可能是T-SQL)查询传递给EF。