Cakephp разбить на страницы с помощью mysql SQL_CALC_FOUND_ROWS

Я пытаюсь сделать разбивку на страницы Cakephp, используя функцию SQL_CALC_FOUND_ROWS в mysql, чтобы возвращать общее количество строк при использовании LIMIT. Надеюсь, это устранит двойной запрос paginateCount(), а затем paginate().

Я поместил это в свой app_model.php, и в основном это работает, но это можно было бы сделать лучше. Может ли кто-нибудь помочь мне понять, как переопределить paginate/paginateCount, чтобы он выполнял только 1 SQL stmt?

/**
 * Overridden paginateCount method, to using SQL_CALC_FOUND_ROWS
 */
public function paginateCount($conditions = null, $recursive = 0, $extra = array()) {
    $options = array_merge(compact('conditions', 'recursive'), $extra);
    $options['fields'] = "SQL_CALC_FOUND_ROWS `{$this->alias}`.*";

В: как получить ТАКОЕ ТО ЖЕ предельное значение, используемое в paginate()?

    $options['limit'] = 1;   // ideally, should return same rows as in paginate()     

Вопрос: можем ли мы как-то заставить результаты запроса разбиваться на страницы напрямую, без дополнительного запроса?

    $cache_results_from_paginateCount = $this->find('all', $options);   
    /*
     * note: we must run the query to get the count, but it will be cached for multiple paginates, so add comment to query
     */
    $found_rows =  $this->query("/* do not cache for {$this->alias} */ SELECT FOUND_ROWS();");
    $count = array_shift($found_rows[0][0]);
    return $count;
}   

/**
 * Overridden paginate method
 */
public function paginate($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array()) {
    $options = array_merge(compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive'), $extra);

В: можно ли как-то получить $cache_results_for_paginate непосредственно из paginateCount()?

    return $cache_results_from_paginateCount;  // paginate in only 1 SQL stmt
    return $this->find('all', $options);   // ideally, we could skip this call entirely
}

person michael    schedule 08.05.2010    source источник
comment
Я просто хочу добавить, что SQL_CALC_FOUND_ROWS часто намного, намного медленнее, чем использование прямого COUNT() и 2 запросов. Причина этого в том, как Mysql ищет данные. В частности, для больших наборов данных решения SQL_CALC_FOUND_ROWS могут выполняться буквально в миллионы раз дольше (100+ секунд против 0,004 секунды для миллиона записей).   -  person Oddman    schedule 22.11.2013


Ответы (1)


Способ, которым я решаю это для разбивки на страницы результатов веб-службы, которая возвращает результаты для текущей страницы, и общее количество результатов, доступных в одном и том же ответе, заключается в том, чтобы скопировать свойство paginate из контроллера в модель в вашем действии контроллера, прежде чем вы вызовите $this->paginate(); чтобы мои настройки разбиения по страницам были доступны в методе модели paginateCount(). Затем в paginateCount() вызовите веб-службу, чтобы получить результаты. Затем сохраните результаты в свойстве модели, а затем верните общее количество доступных результатов. Затем в методе paginate() модели я возвращаю результаты, которые я извлек и сохранил в методе paginateCount().

Возможно, что-то в этом роде сработает и для вас?

person neilcrookes    schedule 11.05.2010
comment
Я использовал класс static var для кэширования paginateResponse, но мне никогда не приходило в голову, что я мог бы также передавать параметры paginate через тот же механизм. Кажется, он работает нормально, пока я могу установить recursive=-1, но если в разбиении на страницы есть ассоциация, то SELECT FOUND ROWS вызывается ПОСЛЕ запросов ассоциации и возвращает неправильные результаты. - person michael; 11.05.2010
comment
также трудно получить правильное значение $fields в find(). в настоящее время используется $fields = is_array($fields)? array_unshift($fields, 'SQL_CALC_FOUND_ROWS') : !empty($fields) ? массив('SQL_CALC_FOUND_ROWS', $поля): SQL_CALC_FOUND_ROWS {$this->alias}.*; но на самом деле проверено только для $fields=;; - person michael; 11.05.2010