Постраничная выборка для динамических данных

Я уже давно внес в свой стиль программирования негласное правило о том, что все перечисления, будь это таблицы или списки, картинки или комментарии, должны загружаться порциями и поддерживать постраничную загрузку. Дело в том, что потом добавить этот функционал бывает гораздо сложнее, нежели чем сделать это сразу. Особенно на этапе разработки этим многие пренебрегают. Основным способом сделать постраничную загрузку является имплементация limit-offset подхода. Сейчас мы будем говорить о данных, возвращаемых из базы, соответственно, примеры я тоже буду строить отталкиваясь от этого. Но limit-offset, естественно, применим и для других случаев.

Если вы не успеваете подготовить отчет по практике, правильнее будет заказать практику у опытных специалистов. Обеспечиваем профессиональное написание отчетов на заказ с полным соблюдением всех методических требований. Оставьте заявку и сэкономьте свое время.

Но что будет, если у нас высока вероятность появления новых данных? Возьмем для примера новости. Например, у нас на данный момент 20 новостей, а мы выводим по 10. Вывели первые 10 новостей, но еще до того как мы перешли к следующим десяти новостям, у нас появилась еще одна новость. Каждая новая запись будет смещать новости, и в результатах 2-й страницы мы увидим дубликат последней записи из первой выборки.

Думаю, наглядно будет представить пример на Zend Framework 1.x. У нас есть наипростейшая таблица NEWS с полями: ID, TIME, TEXT.

public function getNews($offset = 0)
{
    $select = $this->select()
        ->order('time DESC')
        ->limit(10, $offset);

    return $this->fetchAll($select);
}

Это обычный limit-offset. Как же избежать того случая, когда появляются дубликаты? Я для этого запоминаю какое-нибудь уникальное значение последней записи. Это может быть ID, какой-то хэш, пара значений и т.д. Главное — нам нужно отличить последний объект, и в следующей выборке вернуть N элементов после него. А еще очень важно — это то, что это значение должно быть привязано к сортировке.

Вернемся к нашим новостям. Уникальным значением для каждой новости будет ее ID. Этот ID у нас является первичным ключом и имеет опцию auto_increment. Чем новее новость, тем выше ID. В этом случае можно запоминать ID последней новости из выборки, и вместо offset передавать его в функцию.

public function getNews($lastId = 0)
{
    $select = $this->select()
        ->order('time DESC')
        ->limit(10);

    if ($lastId) {
        $select->where($this->getAdapter()->quoteInto('id < ?', $lastId));
    }

    return $this->fetchAll($select);
}

Конечно, в статье разобран очень простой пример, где ID связан с сортировкой. Но, повторюсь, это не обязательно будет ID, найдите такое уникальное значение, которое соответствует сортировке, и используйте его.

Еще такой подход называют постраничной выборкой по курсору. Используется он не везде и не всегда. Но в комментариях, сообщениях, ленте он может быть очень полезен.

Экспертное мнение Алексей

В веб-разработке с 2012 года. Специализируюсь на PHP, JavaScript и архитектуре высоконагруженных проектов. Автор статей по оптимизации, API и базам данных.

Все статьи автора →
Telegram VK Twitter
Читайте также
Беларусь.Яндекс.Деньги
Беларусь.Яндекс.Деньги
Думаю, многие из читателей этого блога занимаются фрилансом, а для многих это и один из основных способов заработка. Конечно же,...
Senior PHP Developer. Упрощенный T9 алгоритм
Senior PHP Developer. Упрощенный T9 алгоритм
Представьте, что вы сейчас проходите собеседование на должность PHP Senior Developer. Очень вероятно, что вас могут попросить реализовать какой-то упрощенный...