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.