Ho una semplice query di paging LINQ contro una sola entità:Entity Framework genera SQL inefficiente per query di paging
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();
mi aspettavo di generare SQL simile a:
select top 50 * from Widgets w where CampaignId = xxx AND CalendarEventId = yyy AND (RecurringEventId IS NULL OR RecurringEventId = zzz) order by w.Id
Quando eseguo quanto sopra query in SSMS, restituisce rapidamente (ha dovuto ricostruire i miei indici prima).
Tuttavia, l'SQL generato è diverso. Esso contiene una query nidificate come illustrato di seguito:
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
La tabella Widget è enorme e la query interna restituisce 100000s di record, causando un timeout.
C'è qualcosa che posso fare per cambiare la generazione? Qualcosa che sto facendo male?
UPDATE
sono finalmente riuscito a refactoring il mio codice per restituire i risultati in tempi relativamente brevi:
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);
nota, la logica page > 0
è usato semplicemente per evitare un parametro non valido in uso; non fa ottimizzazione. Infatti, page > 1
, se valido, non fornisce alcuna ottimizzazione evidente per la prima pagina; dal momento che il Where
non è un'operazione lenta.
si può mostrare la query Piano? Non vedo perché la query interna possa essere recuperata completamente qui. C'è qualcosa di sbagliato nel modo in cui viene eseguito SQL. –
Quanto è veloce l'aggiunta di 'order by' alla query? cio 'seleziona' top 50 * da Widgets dove CampaignId = xxx AND CalendarEventId = yyy ordina per id' – Aducci
Il tuo SQL veloce non ha ORDER BY. Cosa succede se lo aggiungi? – hvd