MySQL-Daten - Der beste Weg, um Paging zu implementieren?


208

Meine iPhone-App stellt eine Verbindung zu meinem PHP-Webdienst her, um Daten aus einer MySQL-Datenbank abzurufen. Eine Anfrage kann 500 Ergebnisse zurückgeben.

Was ist der beste Weg, um Paging zu implementieren und 20 Elemente gleichzeitig abzurufen?

Angenommen, ich erhalte die ersten 20 Anzeigen aus meiner Datenbank. Wie kann ich nun die nächsten 20 Anzeigen anfordern?

Antworten:


309

Aus der MySQL-Dokumentation :

Die LIMIT-Klausel kann verwendet werden, um die Anzahl der von der SELECT-Anweisung zurückgegebenen Zeilen zu beschränken. LIMIT verwendet ein oder zwei numerische Argumente, die beide nichtnegative Ganzzahlkonstanten sein müssen (außer bei Verwendung vorbereiteter Anweisungen).

Bei zwei Argumenten gibt das erste Argument den Versatz der ersten zurückzugebenden Zeile und das zweite die maximale Anzahl der zurückzugebenden Zeilen an. Der Versatz der ersten Zeile ist 0 (nicht 1):

SELECT * FROM tbl LIMIT 5,10;  # Retrieve rows 6-15

Um alle Zeilen von einem bestimmten Versatz bis zum Ende der Ergebnismenge abzurufen, können Sie für den zweiten Parameter eine große Zahl verwenden. Diese Anweisung ruft alle Zeilen von der 96. bis zur letzten Zeile ab:

SELECT * FROM tbl LIMIT 95,18446744073709551615;

Mit einem Argument gibt der Wert die Anzahl der Zeilen an, die vom Anfang der Ergebnismenge zurückgegeben werden sollen:

SELECT * FROM tbl LIMIT 5;     # Retrieve first 5 rows

Mit anderen Worten, LIMIT row_count entspricht LIMIT 0, row_count.


107
Wenn Sie LIMIT für das Paging verwenden, sollten Sie auch ORDER BY angeben.
Mark Byers

10
@shylent: Es ist nichts Falsches daran, die Dokumentation zu zitieren, aber ich stimme zu, dass er hätte erwähnen sollen, dass er die Dokumente kopiert und einen Link zur Originalquelle bereitgestellt hat. Ich bin auch überrascht, dass die Dokumentation Beispiele für die Verwendung von LIMIT ohne ORDER BY enthält ... das scheint eine schlechte Praxis zu sein, um ermutigend zu sein. Ohne ORDER BY gibt es keine Garantie dafür, dass die Bestellung zwischen den Anrufen gleich ist.
Mark Byers

13
Wenn Sie große Ergebnismengen paginieren (und genau dafür ist die Paginierung gedacht - große Ergebnismengen in kleinere Teile aufteilen, oder?), sollten Sie berücksichtigen, dass bei einer Aktion im limit X, YWesentlichen X + Y-Zeilen abgerufen werden und dann X Zeilen von Anfang an werden gelöscht und alle verbleibenden Zeilen werden zurückgegeben. Um es noch einmal zu wiederholen: limit X, Yführt zum Scannen von X + Y-Zeilen.
Shylent

7
Ich mag deine LIMIT 95, 18446744073709551615 Idee nicht. Schau sie dir an OFFSET;-)
CharlesLeaf

5
Dies ist bei der Arbeit mit großen Datenmengen nicht effizient. Prüfen codular.com/implementing-pagination für mutiple Wege whicg für bestimmte scenerio geeignet sind.
Amit

124

Bei 500 Datensätzen ist die Effizienz wahrscheinlich kein Problem. Wenn Sie jedoch über Millionen von Datensätzen verfügen, kann es vorteilhaft sein, eine WHERE-Klausel zu verwenden, um die nächste Seite auszuwählen:

SELECT *
FROM yourtable
WHERE id > 234374
ORDER BY id
LIMIT 20

Die "234374" hier ist die ID des letzten Datensatzes von der vorherigen Seite, die Sie angesehen haben.

Dadurch kann ein Index für die ID verwendet werden, um den ersten Datensatz zu finden. Wenn Sie verwenden, können LIMIT offset, 20Sie feststellen, dass es langsamer und langsamer wird, wenn Sie gegen Ende blättern. Wie gesagt, es spielt wahrscheinlich keine Rolle, ob Sie nur 200 Datensätze haben, aber es kann bei größeren Ergebnismengen einen Unterschied machen.

Ein weiterer Vorteil dieses Ansatzes besteht darin, dass Sie keine Aufzeichnungen verpassen oder eine wiederholte Aufzeichnung erhalten, wenn sich die Daten zwischen den Anrufen ändern. Dies liegt daran, dass das Hinzufügen oder Entfernen einer Zeile bedeutet, dass sich der Versatz aller Zeilen nach der Änderung ändert. In Ihrem Fall ist es wahrscheinlich nicht wichtig - ich denke, Ihr Anzeigenpool ändert sich nicht zu oft und sowieso würde niemand bemerken, wenn er zweimal hintereinander dieselbe Anzeige erhält - aber wenn Sie nach dem "besten Weg" suchen Dann ist dies eine andere Sache, die Sie bei der Auswahl des zu verwendenden Ansatzes berücksichtigen sollten.

Wenn Sie LIMIT mit einem Versatz verwenden möchten (und dies ist erforderlich, wenn ein Benutzer direkt zu Seite 10000 navigiert, anstatt nacheinander durch die Seiten zu blättern), können Sie diesen Artikel über die Suche nach späten Zeilen lesen , um die Leistung von LIMIT mit einem großen Versatz zu verbessern Versatz.


1
Das ist eher so: P Obwohl ich die Implikation absolut ablehne, dass 'neuere' IDs immer größer sind als 'ältere', wird dies meistens tatsächlich der Fall sein und daher denke ich, dass dies 'gut' ist genug'. Wie Sie gezeigt haben, ist eine ordnungsgemäße Paginierung (ohne schwerwiegende Leistungseinbußen bei großen Ergebnismengen) nicht besonders trivial. Wenn Sie schreiben limit 1000000, 10und hoffen, dass sie funktioniert, kommen Sie nicht weiter.
Shylent

1
Der Link zur späten Suche ist sehr nützlich
pvgoddijn

1
Diese Paginierung funktioniert rückwärts, wenn Sie nur "DESC" für die ID-Bestellung verwenden. Ich mag das!
Dennis Heiden

2
Aber wie oft möchten die Leute in der realen Welt nach Ausweis oder Unterstellung nach "Erstellungsdatum" bestellen?
RichieHH

Guter Beitrag, aber area=width*heightes ist nicht nur die Anzahl der Datensätze, die von Bedeutung sein könnten, sondern auch die Größe jedes Datensatzes ist ein Faktor beim Speichern von Ergebnissen im Speicher
nichts ist

43

Definieren Sie OFFSET für die Abfrage. Beispielsweise

Seite 1 - (Datensätze 01-10): Offset = 0, Limit = 10;

Seite 2 - (Datensätze 11-20) Offset = 10, Limit = 10;

und verwenden Sie die folgende Abfrage:

SELECT column FROM table LIMIT {someLimit} OFFSET {someOffset};

Beispiel für Seite 2:

SELECT column FROM table
LIMIT 10 OFFSET 10;

1
Meinen Sie nicht Offset = 10 für Seite 2?
Jenna Maiz

28

Es gibt Literatur darüber:

Das Hauptproblem tritt bei der Verwendung großer OFFSETs auf. Sie vermeiden die Verwendung OFFSETmit einer Vielzahl von Techniken, die von der idAuswahl der Bereiche in derWHERE Klausel bis zu einer Art Caching oder Vorberechnung von Seiten .

Es gibt Lösungsvorschläge bei Use the INDEX, Luke :


1
Das Erhalten der maximalen ID für jede Paging-Abfrage komplexer Abfragen würde zu einer unpraktischen, nicht produktiven Verwendung führen. Rang, Zeilennummer und Paging-Typ zwischen Klauseln helfen bei der Ausführung!
Rizwan Patel

Diese Strategie wird in den bereitgestellten Links berücksichtigt und ordnungsgemäß bewertet. So einfach ist das gar nicht.
Luchostein

Der bereitgestellte Link scheint nur die Basis-Pivot-Uni-Pivot-, Cross-Apply-, Multi-CTE- oder abgeleitete Tabellenmechanik zu erfüllen. wieder stehe ich zu meinem Fall, wenn ich wieder Anfragen in dieser Größenordnung umschreibe, um maximal zu werden, ist architektonischer Overkill! und dann wieder Permutation und Kombination für n "Anzahl der Spalten mit Sortierreihenfolgen!
Rizwan Patel

1
Verstehe ich den Link "Paginierung richtig gemacht" falsch oder ist er bei Abfragen, bei denen gefiltert wird, einfach unpraktisch?
Kontaktmatt

1
@contactmatt Ich teile Ihre Besorgnis. Am Ende scheint es keine Möglichkeit zu geben, die volle Anforderung effizient umzusetzen, sondern lockere Variationen um das Original.
Luchostein

13

Dieses Tutorial zeigt eine großartige Möglichkeit, Paginierung durchzuführen. Effiziente Paginierung mit MySQL

Kurz gesagt, vermeiden Sie die Verwendung von OFFSET oder Large LIMIT


24
vielleicht eine Zusammenfassung geben?
Andrew

Ja, ich würde mich über mehr Aufwand bei der Antwort freuen.
Zorkind

6

Sie können auch tun

SELECT SQL_CALC_FOUND_ROWS * FROM tbl limit 0, 20

Die Zeilenanzahl der select-Anweisung (ohne Limit) wird in derselben select-Anweisung erfasst, sodass Sie die Tabellengröße nicht erneut abfragen müssen. Sie erhalten die Zeilenanzahl mit SELECT FOUND_ROWS ();


1
Dies ist besonders ineffizient. Die *Ergebnisse in mehr Spalten als erforderlich werden abgerufen, und die SQL_CALC_FOUND_ROWSErgebnisse in diesen Spalten werden aus allen Zeilen in der Tabelle gelesen , obwohl sie nicht im Ergebnis enthalten sind. Es wäre viel effizienter, die Anzahl der Zeilen in einer separaten Abfrage zu berechnen, die nicht alle diese Spalten liest. Dann kann Ihre Hauptabfrage nach dem Lesen von 20 Zeilen beendet werden.
Thomasrutter

Bist du sicher? Ich habe die Abfrage anhand einer großen Tabelle SQL_CALC_FOUND_ROWS und einer anderen nicht verwendeten Abfrage zeitlich festgelegt. Ich habe keinen Zeitunterschied gesehen. Auf jeden Fall ist es schneller als 2 Abfragen. 1 - Wählen Sie * von atable limit 0 20 und dann count (*) von atable.
Surajz

1
Ja, ich bin sicher - hier sind weitere Informationen . In allen Fällen, in denen Sie einen Index zum Filtern von Zeilen verwenden, ist SQL_CALC_FOUND_ROWS erheblich langsamer als zwei separate Abfragen. In seltenen Fällen verwenden Sie keinen Index oder (wie in diesem vereinfachten Beispiel) haben Sie keine WHERE-Klausel und es handelt sich um eine MYISAM-Tabelle. Dies macht kaum einen Unterschied (es ist ungefähr gleich schnell).
Thomasrutter


4

Abfrage 1: SELECT * FROM yourtable WHERE id > 0 ORDER BY id LIMIT 500

Abfrage 2: SELECT * FROM tbl LIMIT 0,500;

Abfrage 1 wird mit kleinen oder mittleren Datensätzen schneller ausgeführt. Wenn die Anzahl der Datensätze 5.000 oder mehr beträgt, ist das Ergebnis ähnlich.

Ergebnis für 500 Datensätze:

Abfrage1 dauert 9,9999904632568 Millisekunden

Abfrage2 dauert 19.999980926514 Millisekunden

Ergebnis für 8.000 Datensätze:

Abfrage1 dauert 129.99987602234 Millisekunden

Abfrage2 dauert 160.00008583069 Millisekunden


Sie müssen einen Index erstellen id.
Maarten

6
Wie ist id > 0nützlich?
Michel Jung

1
Wie Maarten sagte, erscheinen diese beiden Abfragen grundsätzlich gleich und zerfallen wahrscheinlich in die gleichen Befehle auf Maschinenebene. Sie müssen ein Indizierungsproblem oder eine wirklich alte Version von MySQL haben.
HoldOffHunger

danke, da ich deine Antwort nicht gesehen habe, musste ich nur die Reihenfolge sehen, in der wo, Reihenfolge und Grenze kommen
Shreyan Mehta

Es wurde ein falsches Beispiel verwendet. Mit offset(das erste zu begrenzende Argument ist Offset) wählen Sie immer noch alle Daten bis zum Limit aus, verwerfen dann diesen Betrag des Offsets und geben dann den Abschnitt zurück, der zwischen offsetund liegt limit. Mit der whereKlausel hingegen legen Sie eine Art Startpunkt für die Abfrage fest und fragen ONLYdiesen bestimmten Teil ab.
Senaps

0

Paging ist einfach, wenn Daten aus einer einzelnen Tabelle abgerufen werden, aber komplex, wenn Daten abgerufen werden, die mehrere Tabellen verbinden. Hier ist ein gutes Beispiel für MySql und Spring:
https://www.easycodeforall.com/zpagination1.jsp


Bitte teilen Sie keine Links zu Websites Dritter, die eines Tages verschwinden könnten. Wenn Sie die Frage des Autors beantworten möchten, geben Sie den entsprechenden Code ein, um ihn zu unterstützen.
Manchester ohne
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.