2016-03-23 2 views
9

Ich habe den folgenden Code, um eine Volltextsuche durchzuführen. Es erstellt eine Abfrage, ruft die Gesamtzahl der von dieser Abfrage zurückgegebenen Zeilen ab und ruft dann die tatsächlichen Zeilen nur für die aktuelle Seite ab.Ausführen von zwei Abfragen in einem einzigen Umlauf in die Datenbank

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

Das funktioniert gut, aber es erfordert zwei Hin- und Rückfahrten zur Datenbank. Gibt es im Interesse der Optimierung des Codes eine Möglichkeit, diese Abfrage zu überarbeiten, so dass sie nur einen Umlauf in die Datenbank hatte?

+1

Meiner Meinung nach Nein. Wenn Sie nicht alle Zeilen im Speicher natürlich speichern möchten. –

+2

Ich glaube nicht, dass du das in einer Rundreise machen kannst, ich denke nur daran, deine Paginierung zu überarbeiten, ohne zu zählen. – JanR

+0

stimme ganz mit @JanR überein. Die einzige Möglichkeit, dies zu erreichen, besteht darin, die Seitennummerierung in einer Abfrage zu verwenden. Wenn die Ergebnisabfrage "null" zurückgibt, sind keine Daten zum Anzeigen vorhanden. –

Antwort

2

Ja, können Sie diese beiden Operationen mit Hilfe der nur eine Abfrage zur Datenbank ausführen:

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

Wenn verwenden Sie MSSQL query so aussehen wil:

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

Nun, ja. Lassen Sie uns alle Daten durch das Netzwerk ziehen, nur um sie wegzuwerfen. Code wie das ist der Grund, einige Programme sind schrecklich langsam. – TomTom

+0

Alle Daten, bist du sicher? Ich nehme nur eine zusätzliche Spalte. Und die Abfrage verwendet keine Unterabfragen für jede Zeile, um die Zählung zu berechnen - stattdessen die einfache Verknüpfung mit der einzigen Zeile. –

+0

Ich sehe nicht, wo Sie alle Daten laden. Ich muss mich jedoch fragen, wie SQL Server die Anzahl der gesamten Menge mit jeder Zeile berechnet. Vielleicht kann es das optimieren. –

1

I‘ Ich bin nicht sicher, ob es genug Wert ist, aber es ist unter den folgenden Einschränkungen machbar:

(1) CurrentPage und RowsPerPage sind nicht von t betroffen Er totalRows Wert.
(2) Die Abfrage wird nach dem Anwenden der Paging-Parameter materialisiert.

Der Trick besteht darin, group by konstanten Wert zu verwenden, der von EF unterstützt wird. Der Code sieht so aus:

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