2016-03-23 6 views
9

Ho il seguente codice per eseguire una ricerca full-text. Crea una query, ottiene il numero totale di righe restituite da quella query e recupera le righe effettive solo per la pagina corrente.Esecuzione di due query in un singolo round trip nel database

// Create IQueryable 
var query = from a in ArticleServerContext.Set<Article>() 
      where a.Approved 
      orderby a.UtcDate descending 
      select a; 

// Get total rows (needed for pagination logic) 
int totalRows = query.Count() 

// Get rows for current page 
query = query.Skip((CurrentPage - 1) * RowsPerPage).Take(RowsPerPage); 

Questo funziona bene, ma richiede due round trip al database. Nell'interesse dell'ottimizzazione del codice, esiste un modo per rielaborare questa query in modo che abbia avuto solo un round trip nel database?

+1

A mio parere No. Se non si desidera memorizzare tutte le righe in memoria, naturalmente. –

+2

Non penso che tu possa farlo in un round trip, l'unico modo che posso pensare è ripensare la tua impaginazione al lavoro senza dover ottenere un conteggio totale – JanR

+0

Completamente d'accordo con @JanR. L'unico modo per ottenere ciò è utilizzare la paginazione in una query. Se la query risultato restituisce "null", non ci sono dati da mostrare. –

risposta

2

Sì, è possibile eseguire questa due operazioni con l'aiuto di una sola query al database:

// Create IQueryable 
var query = from a in ArticleServerContext.Set<Article>() 
      where a.Approved 
      orderby a.UtcDate descending 
      select new { a, Total = ArticleServerContext.Set<Article>().Where(x => x.Approved).Count() }; 

//Get raw rows for current page with Total(Count) field 
var result = query.Skip((CurrentPage - 1) * RowsPerPage).Take(RowsPerPage).ToList(); 

//this data you actually will use with your logic 
var actualData = result.Select(x => x.a).ToList(); 

// Get total rows (needed for pagination logic) 
int totalRows = result.First().Total; 

Se si utilizza MSSQL query wil essere guardare in quel modo:

SELECT 
    [Extent1].[ID] AS [ID], 
    [Extent1].[UtcDate] AS [UtcDate], 
    [Extent1].[Approved] AS [Approved],  
    [GroupBy1].[A1] AS [C1] 
    FROM [dbo].[Articles] AS [Extent1] 
    CROSS JOIN (SELECT 
     COUNT(1) AS [A1] 
     FROM [dbo].[Articles] AS [Extent2] 
     WHERE [Extent2].[Approved]) AS [GroupBy1] 
    WHERE [Extent1].[Approved] 
    ORDER BY [Extent1].[UtcDate] DESC 
+0

Beh, sì. Prendiamo tutti i dati attraverso la rete solo per buttarli via. Codice come quello è la ragione per cui alcuni programmi sono terribilmente lenti. – TomTom

+0

Tutti i dati, sei sicuro? Prendo solo una colonna aggiuntiva. E la query non utilizza sottoquery per ogni riga per calcolare il conteggio - invece è semplice unirsi con l'unica riga. –

+0

Non vedo dove si stanno caricando tutti i dati. Mi chiedo, tuttavia, come SQL Server gestisca il calcolo del conteggio dell'intero set con ogni riga. Forse è in grado di ottimizzarlo. –

1

I' Non sono sicuro se valga la pena, ma è fattibile con i seguenti vincoli:

(1) CurrentPage e RowsPerPage non sono interessati da t lui valore totalRows.
(2) La query viene materializzata dopo l'applicazione dei parametri di paging.

Il trucco è utilizzare il valore costante group by, che è supportato da EF. Il codice è simile al seguente:

var query = 
    from a in ArticleServerContext.Set<Article>() 
    where a.Approved 
    // NOTE: order by goes below 
    group a by 1 into allRows 
    select new 
    { 
     TotalRows = allRows.Count(), 
     PageRows = allRows 
      .OrderByDescending(a => a.UtcDate) 
      .Skip((CurrentPage - 1) * RowsPerPage).Take(RowsPerPage) 
    }; 

var result = query.FirstOrDefault(); 
var totalRows = result != null ? result.TotalRows : 0; 
var pageRows = result != null ? result.PageRows : Enumerable.Empty<Article>(); 
Problemi correlati