Красивый постраничный вывод на php из файла. Постраничная навигация на PHP

друже 5 апреля 2013 в 21:12

Постраничная навигация без использования COUNT и LIMIT

  • Чулан *

Все, кто сталкивался с созданием постраничной навигации с использованием связки mySQL + PHP наверняка замечали, что выборка начинает замедляться при просмотре таблиц со множеством полей (от 100000). В интернете я толком ничего не нашел по этой теме, везде предлагаются классические схемы на основе использования COUNT + LIMIT. Статья рассчитана на тех, кто уже знаком с mySQL и PHP.
Предлагаю несколько иной подход, который даст возможность просматривать страницы с практически любым количеством полей, и тем самым мы попробуем решить проблему «тяжелых» таблиц.

Вместо COUNT мы будем использовать SELECT.
Первое, что нужно сделать - создать столбец с AUTO_INCREMENT и поставить на него Primary Key . Я его для примера назвал id . Это даст нам возможность обойти COUNT и обратиться непосредственно к значению столбца, а именно:

$msg=20; //20 сообщений на страницу $page=(int)$_GET["page"]; //get запрос заносим в $page $q=mysql_query("SELECT max(id) FROM table_name"); //узнаем максимальное значение столбца $str=mysql_result($q,0); //результат в $str $total=(int)(($str - 1) / $msg) + 1; //узнаем общее количество страниц для отображения в навигации $start=$page * $msg - $msg; //здесь узнаем с какой записи начинаем вывод сообщений $stop=$page * $msg; //здесь собсно до какой выводим

Далее рассмотрим как использовать выборку без LIMIT

$q=mysql_query("SELECT * FROM user WHERE id > $start AND id < $stop"); //вот так всё просто while ($body=mysql_fetch_array($q)) //собсно заносим результат запроса в массив echo "

"; for($i=0; $i < $msg; $i++) //этот цикл выводит сообщения в диапазоне id > $start и id < $stop { echo ""; } echo "
".$body[$i]["row_name"]."".$body[$i]["row_name"]."".$body[$i]["row_name"]."
";

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

В общем надеюсь кому-нибудь пригодится, я сам очень долго искал как решить эту проблему. Да, забыл сказать, что здесь говорилось в расчет на использование InnoDB. Вот несколько сравнительных тестов:

При подсчете времени выполнения запроса LIMIT среднее значение 0.09 с. в таблице 1000000 записей
SELECT * FROM table_name LIMIT 999000, 20

При подсчете времени выполнения запроса без LIMIT среднее значение 0.0005 с. в таблице 1000000 записей
SELECT * FROM table_name WHERE id > 999000 AND id < 999020

При подсчете времени выполнения запроса COUNT(*) среднее значение 0.06 с. в таблице 1000000 записей
SELECT COUNT(*) FROM table_name

При подсчете времени выполнения запроса без COUNT(*) среднее значение 0.0003 с. в таблице 1000000 записей
SELECT COUNT(*) FROM table_name

Теги: mysql, php, pagination, LIMIT,COUNT, постраничная навигация

Проблема реализации постраничной навигации часто встает перед начинающими PHP-программистами. К разбиению объёмного текста на отдельные страницы прибегают во многих Web-приложениях от гостевых книг и форумов до различных каталогов. Давайте
решим эту проблему.
Итак, что нам требуется для реализации постраничной навигации? Для примера возьмем гостевую книгу, содержащую несколько сотен сообщений, в которой требуется выводить на страницу Х сообщений.
Рассмотрим задачу более конкретно. Сообщения пользователей хранятся в базе данных post со следующей структурой:

  • id – номер сообщения,
  • text – тело сообщения,
  • name – имя автора,
  • time – время создания.

В итоге нам надо получить вывод Х сообщений на страницу, а также организовать удобную навигацию, например, такую:

<< < ..2|3|4|5|6.. > >>

где 4 - текущая станица. Для того чтобы отслеживать текущую страницу, будем использовать параметр page , передаваемый через URL. К примеру:

Www.myserver.com/index.php?page=X.

Здесь X - номер станицы (для примера, пусть Х будет равно 25).

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

// Устанавливаем соединение с базой данных
include "config.php" ;
// Переменная хранит число сообщений выводимых на станице
$num = 25 ;
// Извлекаем из URL текущую страницу
$page = $_GET [ "page" ];
// Определяем общее число сообщений в базе данных
$result = mysql_query ("SELECT COUNT(*) FROM post" );
$posts = mysql_result ($result , 0 );
// Находим общее число страниц
$total = intval (($posts - 1 ) / $num ) + 1 ;
// Определяем начало сообщений для текущей страницы
$page = intval ($page );
// Если значение $page меньше единицы или отрицательно
// переходим на первую страницу
// А если слишком большое, то переходим на последнюю
if(empty($page ) or $page < 0 ) $page = 1 ;
if($page > $total ) $page = $total ;
// Вычисляем начиная к какого номера
// следует выводить сообщения
$start = $page * $num - $num ;
// Выбираем $num сообщений начиная с номера $start
$result = mysql_query ("SELECT * FROM post LIMIT $start , $num " );
// В цикле переносим результаты запроса в массив $postrow
while ($postrow = mysql_fetch_array ($result ))
?>

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

echo "

" ;
for($i = 0 ; $i < $num ; $i ++)
{
echo "


" ;
}
echo "
" . $postrow [ $i ][ "name" ]. " " . $postrow [ $i ][ "time" ]. "
" . $postrow [ $i ][ "text" ]. "
" ;
?>

Теперь надо организовать навигацию. Формируем составляющие будущей навигации.
Коды стрелки «в начало» и «назад» поместим в одну переменную, также поступим и со стрелками «в конец» и «вперед».

// Проверяем нужны ли стрелки назад
if ($page != 1 ) $pervpage = "<<
. ($page - 1 ) . ">< " ;
// Проверяем нужны ли стрелки вперед
if ($page != $total ) $nextpage = " ">>
. $total . ">>>" ;

// Находим две ближайшие станицы с обоих краев, если они есть
if($page - 2 > 0 ) $page2left = " " . ($page - 2 ) . " | " ;
if($page - 1 > 0 ) $page1left = "" . ($page - 1 ) . " | " ;
if($page + 2 <= $total ) $page2right = " | " . ($page + 2 ) . "" ;
if($page + 1 <= $total ) $page1right = " | " . ($page + 1 ) . "" ;

// Вывод меню
echo $pervpage . $page2left . $page1left . "" . $page . "" . $page1right . $page2right . $nextpage ;

Данный скрипт позволит вывести не только стрелки на «предыдущую страницу» и «следующие страницу», как в статье , но и, при желании, поможет вывести последовательный список ссылок на все страницы сайта. Как это выглядит, вы можете увидеть на моем сайте – просто спуститесь вниз на главной странице сайта.

Посредством phpmyadmin (или sql) создайте таблицу (название на ваше усмотрение, я назвал opt) с двумя полями. Первое поле назовите id , второе str . Оба поля должны иметь числовой тип (int), полю id назначьте auto_increment . Данная таблица будет хранить числовое значение (в поле str), определяющее число постов выводимых на одной странице.

Далее все просто. Делаем запрос и извлекаем это числовое значение, заносим его в переменную, например, $num . Определяем общее число постов (сообщений) в базе данных, заносим это значение в переменную $posts . Находим общее число страниц сайта ($total) и округляем в меньшую сторону. Вычисляем, с какого поста необходимо выводить сообщения (посты) на данной странице ($start). Делаем запрос к таблице с постами и ограничиваем по лимиту ($num), также указываем параметр ($start), с которого начнется вывод записей. Теперь остается соответствующим образом расположить ссылки на соответствующие страницы и, вуаля, готова. Более подробное описание скрипта можно увидеть непосредственно в коде.

$result77 = mysql_query("SELECT str FROM opt", $db); $myrow77 = mysql_fetch_array($result77); // число постов на одной странице $num = $myrow77["str"]; // Извлекаем из URL текущую страницу @$page = $_GET["page"]; // Определяем общее число сообщений в базе данных $result00 = mysql_query("SELECT COUNT(*) FROM posti"); $temp = mysql_fetch_array($result00); $posts = $temp; // Находим общее число страниц $total = (($posts - 1) / $num) + 1; $total = intval($total); // округляем текущую страницу $page = intval($page); // Если переменная $page меньше 0 или пуста // присваиваем $page 1 // А если значение $page выходит за $total, // присваиваем $page значение переменной $total if(empty($page) or $page < 0) $page = 1; if($page > $total) $page = $total; // Вычисляем начиная с какого номера // следует выводить сообщения $start = $page * $num - $num; // Выбираем $num сообщений начиная с номера $start // -----МИНИ ПОСТЫ----- $result = mysql_query("SELECT * FROM posti ORDER BY date DESC LIMIT $start, $num ",$db);

//Выводим все посты в цикле

// Проверяем нужна ли стрелки назад if ($page != 1) $pervpage = "Первая Предыдущая"; // Проверяем нужны ли стрелки вперед if ($page != $total) $nextpage = "Следующая Последняя"; // Находим две ближайшие станицы с обоих краев, если они есть if($page - 2 > 0) $page2left = " ". ($page - 2) ." "; if($page - 1 > 0) $page1left = "". ($page - 1) ." "; if($page + 2 <=$total) $page2right = " ". ($page + 2) .""; if($page + 1 <=$total) $page1right = " ". ($page + 1) .""; // выводим ссылки echo $pervpage.$page2left.$page1left."".$page."".$page1right.$page2right.$nextpage;

Плюс данной навигации в том, что все страницы сайта можно расположить в пределах 3 кликов от главной страницы. Данная особенность может пригодиться, например, при работе с сервисом SAPE.