MySQL Data - лучший способ реализовать пейджинг?


209

Мое приложение для iPhone подключается к моему веб-сервису PHP для извлечения данных из базы данных MySQL. Запрос может вернуть 500 результатов.

Каков наилучший способ реализации подкачки и получения 20 элементов одновременно?

Допустим, я получил первые 20 объявлений из моей базы данных. Теперь, как я могу запросить следующие 20 объявлений?

Ответы:


310

Из документации MySQL :

Предложение LIMIT можно использовать для ограничения количества строк, возвращаемых оператором SELECT. LIMIT принимает один или два числовых аргумента, которые должны быть неотрицательными целочисленными константами (кроме случаев использования подготовленных операторов).

С двумя аргументами первый аргумент задает смещение первой строки для возврата, а второй задает максимальное количество строк для возврата. Смещение исходной строки равно 0 (не 1):

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

Чтобы извлечь все строки от определенного смещения до конца результирующего набора, вы можете использовать некоторое большое число для второго параметра. Этот оператор извлекает все строки из 96-й строки до последней:

SELECT * FROM tbl LIMIT 95,18446744073709551615;

С одним аргументом значение указывает количество строк, возвращаемых из начала результирующего набора:

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

Другими словами, LIMIT row_count эквивалентно LIMIT 0, row_count.


108
При использовании LIMIT для пейджинга вы также должны указать ORDER BY.
Марк Байерс

10
@shylent: Нет ничего плохого в цитировании документации, но я согласен, что он должен был упомянуть, что он копировал документы, и дал ссылку на первоисточник. Также я удивлен, что документация будет включать примеры использования LIMIT без ORDER BY ... это кажется плохой практикой для поощрения. Без ORDER BY нет гарантии, что порядок будет одинаковым между вызовами.
Марк Байерс

13
в любом случае, при разбивке на страницы больших наборов результатов (и для этого разбивка на страницы - разбивать большие наборы результатов на более мелкие порции, верно?), вы должны иметь в виду, что если вы сделаете a limit X, Y, то, по сути, произойдет, что строки X + Y будут извлечены, а затем X строк с начала отбрасываются, и все, что осталось, возвращается. Повторим: limit X, Yрезультаты сканирования X + Y строк.
Shylent

7
Мне не нравится ваша идея LIMIT 95, 18446744073709551615 .. посмотрите OFFSET;-)
CharlesLeaf

5
Это неэффективно при работе с большими данными. Проверьте codular.com/implementing-pagination для нескольких способов, которые подходят для конкретного сценария.
Амит

125

Для 500 записей эффективность, вероятно, не проблема, но если у вас есть миллионы записей, то может быть полезно использовать предложение WHERE для выбора следующей страницы:

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

Здесь "234374" - это идентификатор последней записи с предыдущей страницы, которую вы просматривали.

Это позволит использовать индекс по идентификатору для поиска первой записи. Если вы используете, LIMIT offset, 20вы можете обнаружить, что он становится все медленнее и медленнее, когда вы переходите к концу. Как я уже сказал, это, вероятно, не имеет значения, если у вас есть только 200 записей, но это может иметь значение с большими наборами результатов.

Еще одним преимуществом этого подхода является то, что если данные меняются между вызовами, вы не пропустите записи или получите повторную запись. Это связано с тем, что добавление или удаление строки означает, что смещение всех строк после нее изменяется. В вашем случае это, вероятно, не важно - я думаю, что ваш пул рекламы не меняется слишком часто, и в любом случае никто не заметит, получат ли они одно и то же объявление дважды подряд - но если вы ищете «лучший способ» тогда это еще одна вещь, которую следует иметь в виду при выборе того, какой подход использовать.

Если вы хотите использовать LIMIT со смещением (и это необходимо, если пользователь переходит непосредственно к странице 10000 вместо постраничного перемещения по страницам по одной), вы можете прочитать эту статью о поиске в последних строках, чтобы повысить производительность LIMIT с большим смещение.


1
Это больше похоже на это: P Хотя я абсолютно не одобряю то, что «новые» идентификаторы всегда больше, чем «более старые», в большинстве случаев это действительно так, и я думаю, это «хорошо». достаточно'. В любом случае, да, как вы продемонстрировали, правильное разбиение на страницы (без серьезного снижения производительности на больших наборах результатов) не является особенно тривиальным, и написание limit 1000000, 10и надежда на то, что это сработает, ни к чему вас не приведет.
Shylent

1
ссылка на поздний поиск очень полезна
pvgoddijn

1
Эта нумерация страниц работает в обратном направлении, если вы просто используете «DESC» для упорядочивания идентификаторов. Мне это нравится!
Деннис Хайден

2
но как часто люди хотят сделать заказ по идентификатору или по инсинуации, по «дате создания» в реальном мире?
RichieHH

хороший пост, но area=width*heightне только количество записей может иметь значение, но и размер каждой записи также является фактором при сохранении результатов в памяти
ничего лишнего

43

Определите СМЕЩЕНИЕ для запроса. Например

страница 1 - (записи 01-10): смещение = 0, предел = 10;

страница 2 - (записи 11-20) смещение = 10, предел = 10;

и используйте следующий запрос:

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

пример для страницы 2:

SELECT column FROM table
LIMIT 10 OFFSET 10;

1
Разве вы не имеете в виду смещение = 10 для страницы-2?
Дженна Майз

28

Есть литература об этом:

Основная проблема происходит с использованием больших OFFSETс. Они избегают использования OFFSETс различными методами, начиная от idвыбора диапазона в WHEREпредложении, до некоторого рода страниц кэширования или предварительного вычисления.

Есть предложения по использованию в INDEX, Люк :


1
получение максимального идентификатора для каждого пейджингового запроса сложных запросов может привести к непрактичности, непроизводственное использование делает ранжирование, номер строки и между пейджинговым типом пейджинга помогает в производительности!
Ризван Патель

Эта стратегия учитывается и должным образом оценивается в предоставленных ссылках. Это не так просто.
Лучостейн

предоставленная ссылка, кажется, только выполняет базовую унификацию, перекрестное применение, мульти CTE или производную механику таблиц? снова я поддерживаю свой случай с переписыванием запросов на такую ​​величину снова, поскольку получение максимума - архитектурное излишество! и затем снова перестановка и комбинация для n "числа столбцов с порядками сортировки!
Ризван Патель

1
Я неправильно понимаю, что ссылка «Разбивка на страницы выполнена правильно», или это просто нецелесообразно в любом запросе, который включает в себя фильтрацию.
contactmatt

1
@contactmatt Я разделяю твои опасения. В конце концов, кажется, что нет способа эффективно реализовать все требования, но есть смягченные вариации вокруг оригинала.
Лучостейн

13

Этот урок показывает отличный способ сделать нумерацию страниц. Эффективное разбиение на страницы с использованием MySQL

Короче говоря, избегайте использования OFFSET или большого LIMIT


24
может дать резюме?
Андрей

Да, я был бы признателен за дополнительные усилия в ответ.
Зоркинд

6

Вы также можете сделать

SELECT SQL_CALC_FOUND_ROWS * FROM tbl limit 0, 20

Количество строк в операторе выбора (без ограничения) фиксируется в том же операторе выбора, поэтому вам не нужно снова запрашивать размер таблицы. Вы получаете количество строк, используя SELECT FOUND_ROWS ();


1
Это особенно неэффективно. В *результаты в более столбцов , чем это необходимо , извлекаемых, существо и SQL_CALC_FOUND_ROWSрезультаты в тех столбцах , которые считываются с всех строк в таблице, даже если они не включены в результат. Было бы намного эффективнее рассчитать количество строк в отдельном запросе, который не читает все эти столбцы. Тогда ваш основной запрос может остановиться после прочтения 20 строк.
Томасруттер

Ты уверен? Я рассчитал запрос к большой таблице SQL_CALC_FOUND_ROWS, а другой запрос не использовался. Я не видел никакой разницы во времени. Любыми способами это быстрее, чем делать 2 запроса. 1 - выберите * из допустимого предела 0 20, а затем выберите количество (*) из допустимого.
Сурайз

1
Да, я уверен - здесь больше информации . Во всех случаях, когда вы используете индекс для фильтрации строк, SQL_CALC_FOUND_ROWS значительно медленнее, чем выполнение двух отдельных запросов. В редких случаях вы не используете индекс, или (как в этом упрощенном примере) у вас нет предложения WHERE, и это таблица MYISAM, она не имеет большого значения (примерно с той же скоростью).
Томасруттер


4

Запрос 1: SELECT * FROM yourtable WHERE id > 0 ORDER BY id LIMIT 500

Запрос 2: SELECT * FROM tbl LIMIT 0,500;

Запрос 1 выполняется быстрее с небольшими или средними записями, если число записей равно 5000 или выше, результат аналогичен.

Результат на 500 записей:

Запрос1 занимает 9.9999904632568 миллисекунд

Query2 занимает 19,9999980926514 миллисекунд

Результат для 8000 записей:

Запрос1 занимает 129,99987602234 миллисекунд

Запрос2 занимает 160.00008583069 миллисекунд


Вы должны поставить индекс на id.
Мартен

6
Как это id > 0полезно?
Мишель Юнг

1
Как сказал Мартен, эти два запроса в основном выглядят одинаково и, вероятно, в любом случае разбиваются на одни и те же команды машинного уровня. У вас должна быть проблема с индексированием или действительно старая версия MySQL.
HoldOffHunger

спасибо, поскольку я не видел ваш ответ, мне просто нужно было увидеть порядок, в котором, где, порядок и предел наступает
Shreyan Mehta

неправильный пример был использован. с помощью offset(первый аргумент для limit является смещением), вы по-прежнему выбираете все данные для ограничения, затем отбрасываете эту величину смещения, а затем возвращаете секцию, которая находится между offsetи limit. с помощью whereпредложения, с другой стороны, вы задаете своего рода начальную точку для запроса и запрашиваете ONLYэту конкретную часть.
senaps

0

Пейджинг прост, когда он извлекает данные из одной таблицы, но он сложен, когда извлекает данные, объединяющие несколько таблиц. Вот хороший пример с MySql и Spring:
https://www.easycodeforall.com/zpagination1.jsp


Пожалуйста, не делитесь ссылками на сторонние сайты, которые могут однажды исчезнуть. Если вы хотите ответить на вопрос авторов, разместите соответствующий код, чтобы помочь им.
марочный Манчестер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.