Dane MySQL - najlepszy sposób na wdrożenie stronicowania?

209

Moja aplikacja na iPhone'a łączy się z moją usługą PHP w celu pobrania danych z bazy danych MySQL. Żądanie może zwrócić 500 wyników.

Jaki jest najlepszy sposób wdrażania stronicowania i pobierania 20 elementów jednocześnie?

Załóżmy, że otrzymuję pierwsze 20 reklam z mojej bazy danych. Jak mogę teraz poprosić o następne 20 reklam?

aryaxt
źródło

Odpowiedzi:

309

Z dokumentacji MySQL :

Klauzula LIMIT może służyć do ograniczenia liczby wierszy zwracanych przez instrukcję SELECT. LIMIT przyjmuje jeden lub dwa argumenty liczbowe, które muszą być nieujemnymi stałymi liczbami całkowitymi (z wyjątkiem sytuacji, gdy używane są przygotowane instrukcje).

W przypadku dwóch argumentów pierwszy argument określa przesunięcie pierwszego wiersza do zwrócenia, a drugi określa maksymalną liczbę wierszy do zwrócenia. Przesunięcie początkowego rzędu wynosi 0 (nie 1):

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

Aby pobrać wszystkie wiersze od określonego przesunięcia do końca zestawu wyników, możesz użyć dużej liczby dla drugiego parametru. Ta instrukcja pobiera wszystkie wiersze od 96-tego wiersza do ostatniego:

SELECT * FROM tbl LIMIT 95,18446744073709551615;

Za pomocą jednego argumentu wartość określa liczbę wierszy do zwrócenia od początku zestawu wyników:

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

Innymi słowy, LIMIT liczba_wierszy jest równoważna LIMIT 0, liczba_wierszy.

Faisal Feroz
źródło
107
Korzystając z LIMIT dla stronicowania, należy również określić ORDER BY.
Mark Byers,
10
@shylent: Nie ma nic złego w cytowaniu dokumentacji, ale zgadzam się, że powinien był wspomnieć, że kopiował dokumenty i podał link do oryginalnego źródła. Dziwi mnie też fakt, że dokumentacja zawiera przykłady użycia LIMITU bez ORDER BY ... co wydaje się być złą praktyką zachęcającą. Bez ORDER BY nie ma gwarancji, że kolejność połączeń będzie taka sama.
Mark Byers,
13
w każdym razie, kiedy stronicowanie dużych zestawów wyników (i po to jest paginacja - rozbicie dużych zestawów wyników na mniejsze części, prawda?), należy pamiętać, że jeśli to zrobisz limit X, Y, to w zasadzie dzieje się, że wiersze X + Y są pobierane, a następnie X wierszy od początku jest upuszczanych i wszystko, co pozostało, jest zwracane. Powtórzmy: limit X, Ywyniki skanowania wierszy X + Y.
shylent
7
Nie podoba mi się twój LIMIT 95, 18446744073709551615 pomysł .. spójrz na OFFSET;-)
CharlesLeaf
5
Nie jest to wydajne podczas pracy z dużymi danymi. Sprawdź codular.com/implementing-pagination na wiele sposobów, które są odpowiednie dla konkretnego scenariusza.
Amit
125

Dla 500 rekordów wydajność prawdopodobnie nie jest problemem, ale jeśli masz miliony rekordów, może być korzystne użycie klauzuli WHERE, aby wybrać następną stronę:

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

„234374” tutaj jest identyfikatorem ostatniego rekordu z poprzedniej strony, którą przeglądałeś.

Umożliwi to użycie indeksu na id do znalezienia pierwszego rekordu. Jeśli go użyjesz, LIMIT offset, 20możesz zauważyć, że staje się on coraz wolniejszy, gdy przewijasz stronę do końca. Jak powiedziałem, prawdopodobnie nie będzie to miało znaczenia, jeśli masz tylko 200 rekordów, ale może to zmienić przy większych zestawach wyników.

Kolejną zaletą tego podejścia jest to, że jeśli dane zmienią się między połączeniami, nie przegapisz ani nie uzyskasz powtarzającego się rekordu. Jest tak, ponieważ dodanie lub usunięcie wiersza oznacza przesunięcie wszystkich wierszy po jego zmianie. W twoim przypadku prawdopodobnie nie jest to ważne - myślę, że twoja pula reklam nie zmienia się zbyt często, a zresztą nikt nie zauważy, że otrzymają tę samą reklamę dwa razy z rzędu - ale jeśli szukasz „najlepszego sposobu” to kolejna rzecz, o której należy pamiętać przy wyborze podejścia.

Jeśli chcesz używać LIMIT z przesunięciem (i jest to konieczne, jeśli użytkownik przechodzi bezpośrednio do strony 10000 zamiast stronicowania stron jeden po drugim), możesz przeczytać ten artykuł na temat wyszukiwania w późnych wierszach, aby poprawić wydajność LIMIT z dużym offsetowy.

Mark Byers
źródło
1
Jest to bardziej podobne: P Chociaż absolutnie nie zgadzam się z implikacją, że „nowsze” identyfikatory są zawsze większe, niż „starsze”, przez większość czasu tak rzeczywiście będzie, a więc myślę, że to „dobre” dość'. W każdym razie tak, jak wykazałeś, prawidłowe dzielenie stron (bez znacznego pogorszenia wydajności na dużych zestawach wyników) nie jest szczególnie trywialne i pisanie, limit 1000000, 10mając nadzieję, że zadziała, nigdzie cię nie doprowadzi.
shylent
1
link do późnego wyszukiwania jest bardzo przydatny
pvgoddijn
1
Ta podział na strony działa wstecz, jeśli używasz „DESC” do zamawiania identyfikatorów. Lubię to!
Dennis Heiden
2
ale jak często ludzie chcą składać zamówienia według ID lub insynuacji, według „daty utworzenia” w prawdziwym świecie?
RichieHH
dobry post, ale area=width*heightwięc nie tylko ilość rekordów może mieć znaczenie, ale rozmiar każdego rekordu jest również czynnikiem przy przechowywaniu wyników w pamięci
nic nie jest potrzebne
43

Zdefiniuj PRZESUNIĘCIE dla zapytania. Na przykład

strona 1 - (rekordy 01-10): offset = 0, limit = 10;

strona 2 - (rekordy 11-20) offset = 10, limit = 10;

i użyj następującego zapytania:

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

przykład dla strony 2:

SELECT column FROM table
LIMIT 10 OFFSET 10;
Prabodh Hend
źródło
1
Nie masz na myśli offsetu = 10 dla strony-2?
Jenna Maiz
28

Jest na ten temat literatura:

Główny problem zdarza się przy użyciu dużych OFFSETs. Unikają korzystania OFFSETz różnych technik, od idwyboru zakresu w WHEREklauzuli, do pewnego rodzaju stron buforowania lub wstępnego obliczania.

Sugerowane rozwiązania można znaleźć na stronie Use the INDEX, Luke :

Luchostein
źródło
1
getiing max id dla każdego zapytania stronicowania złożonych zapytań spowodowałoby, że niepraktyczne, nieprodukcyjne użycie ma rangę, numer wiersza i między typami stronicowania klauzul pomaga w wydajności!
Rizwan Patel
Strategia ta jest uwzględniana i odpowiednio oceniana w podanych linkach. To wcale nie jest takie proste.
Luchostein
podany link wydaje się spełniać tylko podstawową funkcję przestawną uni-pivot, cross Apply, multi CTE lub pochodną mechanikę tabel? znowu stoję przy mojej sprawie, przepisując zapytania o takiej wielkości ponownie, aby uzyskać maksimum, to architektoniczna przesada! a potem znowu permutacja i kombinacja dla liczby n "kolumn z porządkami sortowania!
Rizwan Patel
1
Czy nie rozumiem tego linku „Paginacja wykonana we właściwy sposób”, czy też jest to po prostu niepraktyczne w każdym zapytaniu dotyczącym filtrowania.
contactmatt
1
@contactmatt Podzielam twoje obawy. Ostatecznie wydaje się, że nie ma sposobu na skuteczne wdrożenie pełnego wymogu, ale rozluźnione warianty wokół oryginału.
Luchostein
13

Ten samouczek pokazuje doskonały sposób podziału na strony. Wydajna paginacja za pomocą MySQL

Krótko mówiąc, unikaj używania opcji OFFSET lub dużego LIMITU

Bao Le
źródło
24
może podać streszczenie?
Andrew
Tak, byłbym wdzięczny za więcej wysiłku w odpowiedzi.
Zorkind,
6

możesz też zrobić

SELECT SQL_CALC_FOUND_ROWS * FROM tbl limit 0, 20

Liczba wierszy instrukcji select (bez limitu) jest przechwytywana w tej samej instrukcji select, dzięki czemu nie trzeba ponownie sprawdzać wielkości tabeli. Liczba wierszy jest uzyskiwana za pomocą SELECT FOUND_ROWS ();

surajz
źródło
1
Jest to szczególnie nieefektywne. W *wyniku pobierane jest więcej kolumn niż to konieczne, a SQL_CALC_FOUND_ROWSwyniki w tych kolumnach są odczytywane ze wszystkich wierszy w tabeli, nawet jeśli nie są uwzględnione w wyniku. O wiele bardziej wydajne byłoby obliczenie liczby wierszy w osobnym zapytaniu, które nie odczytuje wszystkich tych kolumn. Wtedy twoje główne zapytanie może zostać zatrzymane po przeczytaniu 20 wierszy.
thomasrutter
Jesteś pewny? Zmierzyłem czas zapytania względem dużej tabeli SQL_CALC_FOUND_ROWS i innej nieużywanej kwerendy. Nie widziałem różnicy czasu. W każdym razie jest to szybsze niż wykonywanie 2 zapytań. 1 - wybierz * z możliwego limitu 0 20, a następnie wybierz liczbę (*) z możliwego.
surajz
1
Tak, jestem pewien - oto więcej informacji . We wszystkich przypadkach, gdy używasz indeksu do filtrowania wierszy, SQL_CALC_FOUND_ROWS jest znacznie wolniejszy niż wykonywanie 2 osobnych zapytań. W rzadkich przypadkach, gdy nie używasz indeksu lub (jak w tym uproszczonym przykładzie) nie masz klauzuli WHERE i jest to tabela MYISAM, to robi niewielką różnicę (jest w przybliżeniu z tą samą prędkością).
thomasrutter
Również tutaj jest dyskusja na ten temat na
Stackoverflow
4

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

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

Zapytanie 1 działa szybciej z małymi lub średnimi rekordami, jeśli liczba rekordów wynosi 5000 lub więcej, wynik jest podobny.

Wynik dla 500 rekordów:

Kwerenda 1 zajmuje 9,9999904632568 milisekund

Query2 zajmuje 19,999980926514 milisekund

Wynik dla 8000 rekordów:

Kwerenda 1 zajmuje 129,99987602234 milisekund

Kwerenda 2 zajmuje 160,00008583069 milisekund

Huy
źródło
Musisz umieścić indeks id.
Maarten
6
Jak się id > 0przydaje?
Michel Jung,
1
Jak powiedział Maarten, te dwa zapytania wydają się zasadniczo takie same i prawdopodobnie dzielą się na te same polecenia na poziomie maszyny. Musisz mieć problem z indeksowaniem lub naprawdę starą wersję MySQL.
HoldOffHunger
dzięki, ponieważ nie widziałem twojej odpowiedzi, po prostu musiałem zobaczyć kolejność, w której przychodzi porządek i limit
Shreyan Mehta
użyto niewłaściwego przykładu. za pomocą offset(pierwszy argument limitu to offset), nadal wybierasz wszystkie dane do limitu, następnie odrzucasz tę wartość offsetu, a następnie zwracasz sekcję pomiędzy offseti limit. z whereklauzulą ​​natomiast ustalasz rodzaj punktu początkowego dla zapytania i zapytania do ONLYtej konkretnej części.
senaps
0

Stronicowanie jest proste, gdy pobiera dane z jednej tabeli, ale jest skomplikowane, gdy pobiera dane łączące wiele tabel. Oto dobry przykład z MySql i Spring:
https://www.easycodeforall.com/zpagination1.jsp

Susanta Ghosh
źródło
Nie udostępniaj linków do witryn stron trzecich, które mogą kiedyś zniknąć. Jeśli chcesz odpowiedzieć na pytanie autora, opublikuj odpowiedni kod, aby im pomóc.
Unbranded Manchester