MySQL Data - найкращий спосіб впровадження пейджингу?


209

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

Який найкращий спосіб здійснити пейджінг та отримати 20 елементів одночасно?

Скажімо, я отримую перші 20 оголошень зі своєї бази даних. Тепер як я можу подати заявку на наступні 20 оголошень?

Відповіді:


309

З документації на 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.


107
Використовуючи LIMIT для підкачки, слід також вказати ЗАМОВЛЕННЯ BY.
Марк Байєрс

10
@shylent: Нічого поганого в цитуванні документації, але я згоден, що він повинен був згадати, що він копіював документи та надав посилання на першоджерело. Також я здивований, що в документацію будуть включені приклади використання LIMIT без ЗАМОВЛЕННЯ ... Це здається, що погана практика є обнадійливою. Без ЗАМОВЛЕННЯ НЕ гарантується, що замовлення буде однаковим між дзвінками.
Марк Байєрс

13
у будь-якому випадку, під час пагітування великих наборів результатів (і саме для цього потрібна сторінка - розбийте великі набори результатів на менші шматки, правда?), майте на увазі, що якщо ви робите це 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" - це ідентифікатор останнього запису зі сторінки, яку ви переглядали.

Це дозволить використовувати індекс на id, щоб знайти перший запис. Якщо ви використовуєте, LIMIT offset, 20ви можете виявити, що він стає повільніше і повільніше, коли ви переходите на сторінку в кінці. Як я вже сказав, це, мабуть, не має значення, якщо у вас всього 200 записів, але це може змінити великі набори результатів.

Ще одна перевага цього підходу полягає в тому, що якщо дані змінюються між дзвінками, ви не пропустите записи або отримаєте повторний запис. Це тому, що додавання або видалення рядка означає, що зміщення всіх рядків після його зміни. У вашому випадку це, мабуть, не важливо - я думаю, ваш рекламний пакет не змінюється занадто часто, і все одно ніхто не помітить, якщо вони отримують одне і те ж оголошення двічі поспіль - але якщо ви шукаєте "найкращий спосіб" то це ще одна річ, яку слід пам’ятати, вибираючи, який підхід використовувати.

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


1
Це більше схоже на це: P Хоча я абсолютно не схвалюю наслідок, що "новіші" ідентифікатори завжди більші, ніж "старіші", більшість випадків це справді так, і, я думаю, це "добре достатньо'. У будь-якому випадку, так, як ви продемонстрували, належна сторінка (без серйозної погіршення продуктивності на великих наборах результатів) не особливо банальна і писати, limit 1000000, 10і сподіваючись, що це спрацює, нікуди не дістанеться.
shylent

1
пізній посилання для пошуку дуже корисний
pvgoddijn

1
Ця сторінка працює назад, якщо ви просто використовуєте "DESC" для замовлення ідентифікаторів. Мені це подобається!
Денніс Хайден

2
але як часто люди хочуть замовляти за посвідченням особи чи за інсинуацією, за "створеною датою" в реальному світі?
RichieHH

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

43

Визначте OFFSET для запиту. Наприклад

сторінка 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

Про це є література:

Основна проблема виникає при використанні великих OFFSETs. Вони уникають використання OFFSETрізноманітних методів, починаючи від idвибору діапазону в WHEREпункті, до певного кешування чи попереднього обчислення сторінок.

Запропоновані рішення в Use the INDEX, Luke :


1
getiing max id для кожного запиту підкачки складних запитів призведе до непрактичного, невиробничого використання не займає рангом, номером рядка та між типом підказок підрозділу, який допомагає виконувати!
Rizwan Patel

Ця стратегія враховується і належним чином оцінюється в поданих посиланнях. Це зовсім не так просто.
Luchostein

надане посилання лише здається, що відповідає базовому шарніру uni-pivot, крос-застосуванню, мульти CTE або похідній механіці таблиці? знову я стою біля своєї справи з переписуванням запитів такої величини знову для отримання максиду - це архітектурний надмір! а потім знову перестановка та комбінація для n "кількості стовпців із порядками сортування!
Rizwan Patel

1
Чи я не розумію, що посилання "Пагинація зроблено правильно", чи це просто недоцільно в будь-якому запиті, що включає фільтрацію.
contactmatt

1
@contactmatt Я поділяю ваше розуміння. Врешті-решт, здається, немає можливості ефективно виконати повну вимогу, а розслаблені зміни навколо оригіналу.
Luchostein


6

ви також можете зробити

SELECT SQL_CALC_FOUND_ROWS * FROM tbl limit 0, 20

Кількість рядків оператора select (без обмеження) фіксується в тому самому операторі select, щоб вам не потрібно було повторно запитувати розмір таблиці. Ви отримуєте кількість рядків за допомогою SELECT FOUND_ROWS ();


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

Ти впевнений? Я присвятив запит великій таблиці SQL_CALC_FOUND_ROWS та іншому запиту, який не використовується. Я не бачив різниці у часі. Будь-яким способом це швидше, ніж робити 2 запити. 1 - виберіть * з допустимого обмеження 0 20, а потім виберіть count (*) з atable.
surajz

1
Так, я впевнений - ось додаткова інформація . У всіх випадках, коли ви використовуєте індекс для фільтрування рядків, SQL_CALC_FOUND_ROWS значно повільніше, ніж робити два окремих запити. У рідкісних випадках ви не використовуєте індекс, або (як у цьому спрощеному прикладі) у вас немає пропозиції WHERE, і це таблиця MYISAM, це має незначну різницю (це приблизно однакова швидкість).
thomasrutter


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 мілісекунд

Запит 2 займає 19,999980926514 мілісекунд

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

Запит1 займає 129,99987602234 мілісекунд

Запит2 займає 160,00008583069 мілісекунд


Потрібно поставити індекс id.
Маартен

6
Чим id > 0корисний?
Мішель Юнг

1
Як сказав Маартен, ці два запити виявляються принципово однаковими і, ймовірно, розбиваються на ті ж самі команди на рівні машин. У вас повинна бути проблема індексації або дійсно стара версія MySQL.
HoldOffHunger

дякую, як я не бачив вашої відповіді, мені просто потрібно було побачити порядок, в якому приходить порядок та межа
Shreyan Mehta

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

0

Підказка проста, коли вона отримує дані з однієї таблиці, але вона є складною, коли вона отримує дані, що об'єднуються в декілька таблиць. Ось хороший приклад з MySql та Spring:
https://www.easycodeforall.com/zpagination1.jsp


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