我在EF CodeFirst中使用
Take
和Skip
语句来实现分页(如Zoran Maksimovic在这篇文章中所说),这些语句导致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?
没有看到你的完整架构等,很难确定,但对我来说,这表明缺少索引1。
运行包含实际执行计划的查询,看看 SSMS 告诉你什么 - 它通常建议一个额外的索引。
我有一个包含数百万条记录的数据集,并花了大约 3 天的时间使用 LINQpad、SSMS 和执行计划的详细分析来优化每个查询。最后,我仅通过添加 3 个额外索引就节省了大约 96% 的执行时间。
1 这是一个线索,表明索引是必需的/当您可以限制范围并提高性能时会有帮助 - 因为这表明正在执行扫描,而我们不喜欢扫描...
这是算法查询生成的双刃剑:查询的好坏取决于算法。改变这种情况的唯一方法是算法决定做一些不同的事情——也许使用不同版本的 ORM 工具。当然,“不同”并不总是等同于“更好”:)
对于重要的查询,经验丰富的 SQL 开发人员在配备正确的工具时(SSMS 提供了您所需的大部分功能),通常能够胜过许多生成的查询。当然,经验不足的 SQL 开发人员可能最终会导致查询执行错误的事情。
大多数 ORM 提供将原始 SQL 查询传递到引擎中的选项 - 但这确实会失去可移植性。如果这是一个问题,另一种选择是存储过程之类的东西,即使在不同数据库的不同实现上,API 也可能是相似的。
您需要在这里问的问题是:
如果答案为“否”和“是”,您应该可以将原始 SQL(大概是 T-SQL)查询传递给 EF。