Разбиение страниц MySQL без двойного запроса?

Мне было интересно, есть ли способ получить количество результатов из запроса MySQL и в то же время ограничить результаты.

Как работает разбиение на страницы (как я понимаю), сначала я делаю что-то вроде

query = SELECT COUNT(*) FROM `table` WHERE `some_condition`

После получения num_rows (query) у меня есть количество результатов. Но тогда, чтобы фактически ограничить мои результаты, я должен выполнить второй запрос, например:

query2 = SELECT COUNT(*) FROM `table` WHERE `some_condition` LIMIT 0, 10

Мой вопрос: все равно, чтобы получить общее количество результатов, которые будут выданы, и ограничить результаты, возвращаемые в одном запросе? Или более эффективный способ сделать это. Спасибо!

Ответ 1

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

Другой способ - использовать предложение SQL_CALC_FOUND_ROWS, а затем вызвать SELECT FOUND_ROWS(). кроме того, что вы должны поместить вызов FOUND_ROWS() после этого, есть проблема с этим: есть ошибка в MySQL, что это щекочет который влияет на запросы ORDER BY, делая его намного медленнее на больших таблицах, чем наивный подход к двум запросам.

Ответ 2

Я почти никогда не делаю двух запросов.

Просто верните еще одну строку, чем нужно, только отобразите 10 на странице, и если их больше, чем отображено, отобразите кнопку "Далее".

SELECT x, y, z FROM `table` WHERE `some_condition` LIMIT 0, 11
// iterate through and display 10 rows.

// if there were 11 rows, display a "Next" button.

Ваш запрос должен быть возвращен в порядке, соответствующем первому. Скорее всего, большинство людей не заботятся о том, чтобы перейти на страницу 236 из 412.

Когда вы выполняете поиск в Google, и ваши результаты не на первой странице, вы, вероятно, перейдите на страницу два, а не девять.

Ответ 3

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

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

Например, ответы на вопрос stackoverflow редко разливаются на вторую страницу. Комментарии к ответу редко разливаются в пределах 5 или около того, чтобы показать их все.

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

Ответ 4

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

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

Если вы берете подход с двумя запросами, первый выбирает только COUNT (*), а не на самом деле извлекает и фактические данные, это может быть выполнено гораздо быстрее, потому что оно обычно может использовать индексы и не нужно извлекать фактические данные для каждой строки, на которую он смотрит. Затем во втором запросе нужно посмотреть только первые строки $offset + $limit и затем вернуться.

Это сообщение из блога производительности MySQL объясняет это далее:

http://www.mysqlperformanceblog.com/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/

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

Ответ 5

query = SELECT col, col2, (SELECT COUNT(*) FROM `table`) AS total FROM `table` WHERE `some_condition` LIMIT 0, 10

Ответ 6

Мой ответ может быть запоздалым, но вы можете пропустить второй запрос (с лимитом) и просто фильтровать информацию через ваш конец script. В PHP, например, вы можете сделать что-то вроде:

if($queryResult > 0) {
   $counter = 0;
   foreach($queryResult AS $result) {
       if($counter >= $startAt AND $counter < $numOfRows) {
            //do what you want here
       }
   $counter++;
   }
}

Но, конечно, когда у вас есть тысячи записей, которые нужно учитывать, они становятся неэффективными очень быстро. Предпочитаемый подсчет может быть хорошей идеей для изучения.

Здесь хорошо читайте по теме: http://www.percona.com/ppc2009/PPC2009_mysql_pagination.pdf

Ответ 7

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

SELECT Movie.*, (
    SELECT Count(1) FROM Movie
        INNER JOIN MovieGenre 
        ON MovieGenre.MovieId = Movie.Id AND MovieGenre.GenreId = 11
    WHERE Title LIKE '%s%'
) AS Count FROM Movie 
    INNER JOIN MovieGenre 
    ON MovieGenre.MovieId = Movie.Id AND MovieGenre.GenreId = 11
WHERE Title LIKE '%s%' LIMIT 8;

Заметьте, что я не эксперт по базам данных, и надеюсь, что кто-то сможет оптимизировать это немного лучше. Поскольку он стоит на нем прямо из интерфейса командной строки SQL, они оба берут ~ 0.02 секунды на моем ноутбуке.

Ответ 8

SELECT * 
FROM table 
WHERE some_condition 
ORDER BY RAND()
LIMIT 0, 10