2015-12-30 14 views
9

Also, Code ist sehr einfach:Entity Framerowk Überspringen/Take ist sehr langsam, wenn Zahl zu überspringen groß ist

var result = dbContext.Skip(x).Take(y).ToList(); 

Wenn x groß (~ 1.000.000) ist, Abfrage sehr langsam ist. y ist klein - 10, 20.

SQL-Code hierfür lautet: (von SQL Profiler)

SELECT ... 
FROM ... 
ORDER BY ... 
OFFSET x ROWS FETCH NEXT y ROWS ONLY 

Die Frage ist, ob jemand weiß, wie solchen Paging zu beschleunigen? Danke.

+0

Was passiert, wenn Sie eine Bestellung vor dem Überspringen einer Spalte mit Index durchführen? – Shyju

+0

Wofür benötigen Sie 1 Mühlenreihen? – ErikEJ

Antwort

3

Ich denke, OFFSET .. FETCH ist sehr nützlich beim Durchsuchen der ersten Seiten aus Ihren großen Daten (was sehr häufig in den meisten Anwendungen passiert) und haben einen Leistungsnachteil bei der Abfrage hoher Seiten von großen Daten.

Weitere Informationen zu Leistung und Alternativen zu OFFSET .. FETCH finden Sie unter article.

Versuchen Sie, so viele Filter auf Ihre Daten anzuwenden, bevor Sie Paging anwenden, sodass Paging für ein kleineres Datenvolumen ausgeführt wird. Es ist schwer vorstellbar, dass der Benutzer nicht durch 1M Zeilen navigieren möchte.

+0

Vielen Dank für diesen Artikel, es hilft wirklich. Natürlich sollte der resultierende Datensatz viel kleiner sein, da es keinen Sinn macht, durch alle diese Datensätze zu navigieren. Habe gerade dieses Problem gefunden und habe mich gefragt, warum es passiert. – berliner

4

Navigieren obwohl eine Million Datensätze in einer Datenbank immer langsam im Vergleich zu anderen Möglichkeiten ist, muss die Datenbank eine Million Datensätze "überspringen", indem sie das Ergebnis im Speicher erstellt und dann die ersten Millionen Zeilen verwerfen.

Haben Sie über eine Nicht-SQL-Alternative (solr, lucene usw.) nachgedacht, um zumindest die IDs Ihrer Zeilen zu erhalten und dann eine ID wo in() query zu verwenden?

Alternativ könnten Sie eine Suchtabelle (Kochtabelle) Ihrer Haupttabelle mit nur den minimalen Daten und den Ids haben, so dass Sie das überspringen und die IDs bekommen und die große Tabelle mit denen abfragen können.

3

Sie sind richtig, Skip(). Take() Ansatz ist langsam auf SQL Server. Als ich merkte, dass ich einen anderen Ansatz benutzt habe, hat es gut funktioniert. . Stattdessen Linq Überspringen der Verwendung von() Nehmen Sie() - was den Code schreibt Sie zeigte -, schreibe ich explizit die SQL als:

select top NTake ... from ... order by ... where orderedByValue > lastRetrievedValue 

dieses schnell arbeitet (unter Berücksichtigung Ich habe Index für die durch Spalte geordnet (s)).

+0

Dies ist eine sehr gute Problemumgehung. Ich werde einige Benchmarks damit machen. Vielen Dank. –

+0

Sehr nette Idee. Aber zuallererst wird lastRetrievedValue 0 sein –

+0

Aber es gibt ein Problem. Angenommen, es gibt eine Spalte, die dieselben Werte haben kann. Diese Zeit orderedByValue> lastRetrievedValue wird vom nächstgrößeren Wert genommen, nicht von der Stelle, an der sie das letzte Mal endete –

2

Möglicherweise fehlt Ihnen ein Index für Ihre Tabelle (oder Sie haben zu viele davon), was dazu führt, dass die SQL-Sortierung/-Filterung diese Zeilen nicht effizient überspringen kann (oder im Fall zu vieler Indizes) schade, einen guten Index für den Job zu wählen).

Versuchen Sie, die SQL-Abfrage Testen direkt:

  • überprüfen Sie die tatsächlichen Ausführungsplan,
  • Prüfung für Indexhinweise fehlen (erfordern die Abfrage als eine nicht-dynamische SQL-Abfrage neu zu schreiben, wenn ef ausgegeben hat einige dynamische Abfrage-Code),
  • Check für Sorten in Temp db verschütten,
  • ...

Also, kurz gesagt, Scheck ob das Problem wirklich ein Entity-Framework-Problem oder ein "reines" SQL-Problem ist.

Randnotiz: EF-Probleme offset/fetch ausgelagert Abfragen nur, wenn es für Dialekt SQL2012 konfiguriert ist. Für frühere Dialekte wird stattdessen row_number() verwendet.

+0

Aus dem [Artikel] (http://www.mssqlgirl.com/paging-function-performance-in-sql-server-2012.html) von @Alexei scheint es, dass "row_number()" viel besser funktioniert als 'OFFSET' -' FETCH' in Ihrem Fall. Sie können also versuchen, den EF-Dialekt auf "SQL 2008" herabzustufen, um dies zu überprüfen. Das wäre meiner Meinung nach kein schöner Hack. –

Verwandte Themen