2010-12-16 5 views
0

Ich suchte einige LINQ zu SQL-Code, der für eine ausgelagerte Tabelle verwendet wird. Darin müssen eine Untergruppe von Datensätzen und die Gesamtzahl der Datensätze in der Datenbank zurückgegeben werden. Der Code sieht wie folgt aus:Linq to sql - Optimale Möglichkeit, Sub-Satz von Datensätzen und die Gesamtanzahl der Datensätze

var query = (from p in MyTable select new {p.HostCable, p.PatchingSet}); 
int total = query.ToList().Count; 
query = query.Skip(5).Take(10); 

ich ein wenig graben wollte, was passiert, wenn diese ausgeführt wird, sehe ich, dass 2-Abfragen passieren - eine alle Zeilen aus der db zu bekommen, und man die Teilmenge zu erhalten. Unnötig zu sagen, dass die Auswirkungen auf die Leistung, alle Datensätze zu erhalten, nicht gut sind. Ich vermute, dass die "ToList" die Ausführung der Abfrage erzwingt, dann wird die Count-Methode für die gesamte Liste ausgeführt.

In die Erklärung Refactoring effizienter zu sein - das ist meine verbesserte Version:

int total = MyTable.Count(); 
var query = (from p in MyTable select new {p.HostCable, p.PatchingSet}).Skip(5).Take(10); 

Dies führt zu einem SQL-Hit für ein „Select K ..“ und dann eine SQL für die eigentlichen Hit Auswahl von Datensätzen.

Ist das optimal, gibt es bessere Lösungen?

Danke!

Antwort

1
int total = query.Count(); 

Anstatt alles auswählen und dann eine Zählung der Objekte im Speicher tut dies die Zählung über eine Abfrage durchführen wird und das Rück nur die Zahl, die viel schneller sein sollten.

Als Reaktion auf Ihr Update, glaube ich nicht, dass Sie einen viel besseren Weg bekommen werden. Es läuft letztlich auf zwei Abfragen hinaus, eine, um die Gesamtzahl der Datensätze zu erhalten, und eine andere, um eine Teilmenge davon zu erhalten.

+0

Ich stimme zu. Dies wäre der beste Weg. Eine count (*) -Operation ist normalerweise ziemlich schnell (mit den richtigen Indizes). – Steven

2

Von dem, was ich gesehen habe, ist dies in LINQ to SQL ohne ein bisschen Hacking nicht möglich.

Ich habe festgestellt, dass diese Methode funktioniert. Eine kurze Zusammenfassung dieser Methode: Ich konvertiere das Objekt IQueryable in ein Befehlsobjekt und modifiziere den Befehlstext, um die Gesamtzahl in die Ergebnismenge aufzunehmen. Die ursprüngliche LINQ to SQL-Konverter-SQL-Abfrage verwendet die ROW_NUMBER() OVER()-Syntax, um die Zeilen zu pagen, ich füge nur COUNT(*) OVER() hinzu, um die Gesamtzahl zu erhalten.

Fügen Sie diese Methode zu Ihrer DataContext-Klasse hinzu.

public IEnumerable<TWithTotal> ExecutePagedQuery<T, TWithTotal>(IQueryable<T> query, int pageSize, int pageNumber, out int count) 
    where TWithTotal : IWithTotal 
{ 
    var cmd = this.GetCommand(query.Skip(pageSize * pageNumber).Take(pageSize)); 
    var commandText = cmd.CommandText.Replace("SELECT ROW_NUMBER() OVER", "SELECT COUNT(*) OVER() AS TOTALROWS, ROW_NUMBER() OVER"); 
    commandText = "SELECT TOTALROWS AS TotalCount," + commandText.Remove(0, 6); 
    cmd.CommandText = commandText; 

    var reader = cmd.ExecuteReader(); 

    var list = this.Translate<TWithTotal>(reader).ToList(); 

    if (list.Count > 0) 
     count = list[0].TotalCount; 
    else 
     count = 0; 

    return list; 
} 

Sie müssen eine neue Klasse erstellen, die die IWithTotal Schnittstelle implementiert alle Eigenschaften des ursprünglichen Objekts enthält.

UPDATE: Sie können nicht verknüpfte und nicht zugeordnete Spalten in LINQ to SQL mischen.

public interface IWithTotal 
{ 
    int TotalCount { get; set; } 
} 

public class Project : IWithTotal 
{ 
    public int TotalCount { get; set; } 
    public int ProjectID { get; set; } 
    public string Name { get; set; } 
} 

Die DataContext.Translate hat einige Anforderungen so sicherstellen, dass Ihre Abfrage diese Anforderungen erfüllt, wenn Sie irgendwelche Fragen haben.

Abhängig von der Komplexität der Abfrage sollten Sie in der Lage sein, eine Leistungssteigerung zu sehen. Unten sind Metriken von einem Test, den ich mit einer Zählungsabfrage, einer seitenweise ausgewählten Abfrage und einer seitenweisen Auswahl mit Zählabfrage durchgeführt habe. Die Prozentsätze sind die Abfragekosten relativ zum Stapel.

Count - 6% 
Select with paging - 47% 
Select with paging + Count - 48%