Entity Framework generiert ineffizientes SQL für eine seitenbasierte Abfrage

8

Ich habe eine einfache seitenbezogene Linq-Abfrage für eine Entität:

%Vor%

Ich habe erwartet, dass SQL ähnlich wie folgt generiert wird:

%Vor%

Wenn ich die obige Abfrage in SSMS ausführe, kommt sie schnell zurück (musste meine Indizes zuerst neu aufbauen).

Das generierte SQL ist jedoch anders. Es enthält eine verschachtelte Abfrage wie folgt:

%Vor%

Die Widgets-Tabelle ist enorm und die innere Abfrage gibt 100000 Datensätze zurück, was zu einem Timeout führt.

Gibt es etwas, was ich tun kann, um die Generation zu ändern? Was mache ich falsch?

AKTUALISIEREN

Ich habe es schließlich geschafft, meinen Code zu refaktorieren, um die Ergebnisse relativ schnell zurückzugeben:

%Vor%

Beachten Sie, dass die page > 0 -Logik einfach verwendet wird, um zu verhindern, dass ein ungültiger Parameter verwendet wird. es macht keine Optimierung. Tatsächlich liefert page > 1 , obwohl gültig, keine merkliche Optimierung für die erste Seite; da% code_de keine langsame Operation ist.

    
Kev 21.07.2015, 21:05
quelle

3 Antworten

1

Vor SQL Server 2012 ist der generierte SQL-Code die beste Methode zum Ausführen des Seitenumbaus. Ja, es ist schrecklich und sehr ineffizient, aber es ist das Beste, was Sie tun können, wenn Sie Ihren eigenen SQL-Skript manuell schreiben. Es gibt Tonnen von digitaler Tinte darüber im Netz. Einfach googlen.

Auf der ersten Seite kann dies optimiert werden, indem Skip und nur Take nicht ausgeführt werden, aber auf jeder anderen Seite, auf der Sie sich befinden.

Ein Workaround könnte darin bestehen, eine eigene Zeilennummer in Persistenz zu generieren (eine automatische Identität könnte funktionieren) und einfach where(widget.number > (page*rows) ).Take(rows) im Code verwenden. Wenn Ihr widget.number einen guten Index enthält, sollte die Abfrage sehr schnell sein. Aber , dies unterbricht die dynamische orderBy .

Allerdings kann ich in Ihrem Code sehen, dass Sie immer nach widget.id bestellen. Wenn also dynamic orderBy nicht erforderlich ist, könnte dies eine gültige Problemumgehung sein.

  

Nehmen Sie Ihre eigene Medizin?

Könnten Sie mich fragen?

Nein, werde ich nicht. Der beste Weg, um damit fertig zu werden, ist ein Persistence Read-Model, in dem Sie sogar eine Tabelle pro Widget orderBy mit einem eigenen widget.number haben können. Das Problem ist, dass das Modellieren eines Systems mit einem Persistenz-Lesemodell nur für dieses Problem zu verrückt ist. Ein Lesemodell ist Teil des Gesamtdesigns Ihres Systems und erfordert, dass es von Anfang an beim Entwurf und der Entwicklung eines Systems berücksichtigt wird.

    
jlvaquero 22.07.2015, 10:11
quelle
1

Die generierte Abfrage ist so komplex und verschachtelt, weil Sie die Skip-Methode verwendet haben. In T-SQL Take ist leicht erreichbar mit nur Top, aber das ist nicht der Fall mit Skip - um es anzuwenden Sie brauchen row_number und deshalb gibt es eine verschachtelte Abfrage - innere gibt Zeilen mit row_number und äußere Filter sie richtig zu bekommen Anzahl der Zeilen. Ihre Anfrage:

%Vor%

fehlt das Überspringen von Anfangszeilen. Um die Abfrage sehr effizient zu halten, wäre es am besten, Take und Skip nicht zu verwenden, um den Paging nach Bedingung auf Id zu halten, da Sie Ihre Zeilen basierend auf diesem Feld für Paging bestellen:

%Vor%     
mr100 22.07.2015 11:21
quelle
0

AFAIK Sie können die von der Entität generierte Abfrage nicht ändern. Obwohl Sie Entity zwingen können, eine rohe SQL-Abfrage auszuführen:

Ссылка

Sie können auch gespeicherte Prozeduren verwenden:

Ссылка

Selbst wenn es eine Chance gibt, die generierte Abfrage IMO zu ändern, würde sie in den Wind spucken. Ich wette, dass es einfacher ist, die SQL-Abfrage selbst zu schreiben.

    
Landeeyo 22.07.2015 09:10
quelle