2016-03-23 38 views
9

Mam następujący kod do wyszukiwania pełnotekstowego. Tworzy zapytanie, pobiera całkowitą liczbę wierszy zwróconych przez to zapytanie, a następnie pobiera rzeczywiste wiersze tylko dla bieżącej strony.Wykonanie dwóch zapytań w jednym cyklu podróży do bazy danych

// 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); 

To działa dobrze, ale wymaga dwóch podróży w obie strony do bazy danych. Czy w celu zoptymalizowania kodu istnieje jakiś sposób na przerobienie tego zapytania, aby miał on tylko jedną podróż w obie strony do bazy danych?

+1

Moim zdaniem Nie. Jeśli nie chcesz oczywiście przechowywać wszystkich wierszy w pamięci. –

+2

Nie sądzę, żebyś mógł to zrobić podczas jednej podróży w obie strony, tylko sposób, w jaki mogę myśleć, to przemyślenie strony do pracy bez konieczności uzyskania całkowitej liczby. – JanR

+0

Całkowicie zgadzam się z @JanR. Jedynym sposobem na osiągnięcie tego jest użycie paginacji w jednym zapytaniu. Jeśli zapytanie wynikowe zwróci 'null', to nie ma danych do wyświetlenia. –

Odpowiedz

2

Tak, to można wykonać dwie operacje za pomocą tylko jednego zapytania do bazy danych:

// 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; 

Jeśli używasz MSSQL query wil wyglądać w ten sposób:

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

Cóż, tak. Przeciągnijmy wszystkie dane przez sieć, aby je wyrzucić. Kod taki jak ten powoduje, że niektóre programy są strasznie wolne. – TomTom

+0

Wszystkie dane, czy jesteś pewien? Biorę tylko jedną dodatkową kolumnę. Zapytanie nie używa podkwerend dla każdego wiersza do obliczenia Count - zamiast tego proste sprzężenie z jednym tylko wierszem. –

+0

Nie widzę, gdzie ładujesz wszystkie dane. Zastanawiam się jednak, w jaki sposób SQL Server obsługuje obliczanie liczby całego zestawu z każdym wierszem. Być może jest w stanie to zoptymalizować. –

1

I” nie jestem pewien, czy jest wystarczająco dużo, ale można go wykonać pod następującymi ograniczeniami:

(1) CurrentPage i RowsPerPage nie mają na on wartość totalRows.
(2) Zapytanie zostanie zrealizowane po zastosowaniu parametrów stronicowania.

Sztuką jest użycie stałej wartości group by obsługiwanej przez EF. Kod wygląda tak:

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>();