我对一个实体有一个简单的分页linq查询:
var data = (from t in ctx.ObjectContext.Widgets
where t.CampaignId == campaignId &&
t.CalendarEventId == calendarEventId
(t.RecurringEventId IS NULL OR t.RecurringEventId = recurringEventId)
select t);
data = data.OrderBy(t => t.Id);
if (page > 0)
{
data = data.Skip(rows * (page - 1)).Take(rows);
}
var l = data.ToList();
我希望它能生成类似于:
的SQLselect top 50 * from Widgets w where CampaignId = xxx AND CalendarEventId = yyy AND (RecurringEventId IS NULL OR RecurringEventId = zzz) order by w.Id
当我在SSMS中运行上述查询时,它会快速返回(必须先重建我的索引)。
但是,生成的SQL是不同的。它包含一个嵌套查询,如下所示:
SELECT TOP (50)
[Project1].[Id] AS [Id],
[Project1].[CampaignId] AS [CampaignId]
<redacted>
FROM ( SELECT [Project1].[Id] AS [Id],
[Project1].[CampaignId] AS [CampaignId],
<redacted>,
row_number() OVER (ORDER BY [Project1].[Id] ASC) AS [row_number]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[CampaignId] AS [CampaignId],
<redacted>
FROM [dbo].[Widgets] AS [Extent1]
WHERE ([Extent1].[CampaignId] = @p__linq__0) AND ([Extent1].[CalendarEventId] = @p__linq__1) AND ([Extent1].[RecurringEventId] = @p__linq__2 OR [Extent1].[RecurringEventId] IS NULL)
) AS [Project1]
) AS [Project1]
WHERE [Project1].[row_number] > 0
ORDER BY [Project1].[Id] ASC
Widgets表非常庞大,内部查询返回100000条记录,导致超时。
我能做些什么来改变这一代人?我做错了什么?
更新
我终于设法重构我的代码以相对快速地返回结果:
var data = (from t in ctx.ObjectContext.Widgets
where t.CampaignId == campaignId &&
t.CalendarEventId == calendarEventId
(t.RecurringEventId IS NULL OR t.RecurringEventId = recurringEventId)
select t)).AsEnumerable().Select((item, index) => new { Index = index, Item = item });
data = data.OrderBy(t => t.Index);
if (page > 0)
{
data = data.Where(t => t.Index >= (rows * (page - 1)));
}
data = data.Take(rows);
注意,page > 0
逻辑仅用于防止使用无效参数;它没有优化。事实上,page > 1
虽然有效,却没有为第一页提供任何明显的优化;因为Where
不是一个缓慢的操作。
答案 0 :(得分:1)
在SQL Server 2012之前,生成的SQL代码是执行pagging的最佳方式。是的,它非常糟糕且非常低效,但是即使手工编写自己的SQL scritp也是最好的。网上有大量的数字墨水。只是谷歌吧。
在第一页中,这可以优化而不是Skip
而只是Take
,但在任何其他页面中你都可以。{/ p>
workarround可以在持久性中生成您自己的row_number(自动标识可以工作),并在代码中执行where(widget.number > (page*rows) ).Take(rows)
。如果您的widget.number
中有一个好的索引,那么查询应该非常快。 但,这会破坏动态orderBy
。
但是,我可以在您的代码中看到您始终按widget.id
排序;因此,如果动态orderBy
不是必需的,那么这可能是一种有效的解决方法。
你可以问我吗。 不,我不会。解决此问题的最佳方法是使用持久性读取模型,即使每个窗口小部件具有一个表格,也可以使用自己的你会自己吃药吗?
widget.number
。问题是为这个问题建立一个具有持久性读取模型的系统是太疯狂了。拥有读模型是系统整体设计的一部分,需要从系统设计和开发的最初阶段就考虑到它。
答案 1 :(得分:1)
生成的查询非常复杂且嵌套,因为您使用了Skip方法。在T-SQL中,只需使用Top就可以轻松实现Take,但Skip不是这样 - 要应用它你需要row_number,这就是为什么有一个嵌套查询 - 内部返回row_number和外部过滤它们以获得正确行数。您的查询:
proxy: {
type: 'ajax',
// ....
listeners: {
'exception': function(proxy, request) {
if (request.status === 0) {
timeoutCallback();
}
}
}
}
缺少跳过初始行。为了保持查询非常有效,最好不要使用Take和Skip来按ID继续按条件进行分页,因为您要根据该字段对行进行分页:
select top 50 * from Widgets w where CampaignId = xxx AND CalendarEventId = yyy AND (RecurringEventId IS NULL OR RecurringEventId = zzz) order by w.Id
答案 2 :(得分:0)
AFAIK您无法更改实体生成的查询。虽然您可以强制实体运行原始SQL查询:
https://msdn.microsoft.com/en-us/data/jj592907.aspx
您还可以使用存储过程:
https://msdn.microsoft.com/en-us/data/gg699321.aspx
即使有机会改变生成的查询IMO,它也会随波逐流。我敢打赌,更容易自己编写SQL查询。