作者:夜猫子(yeaha@163.com)
1、前言
分页显示是一种非常常见的浏览和显示大量数据的方法,属于web编程中最常处理的事件之一。对于web编程的老手来说,编写这种代码实在是和呼吸一样自然,但是对于初学者来说,常常对这个问题摸不着头绪,因此特地撰写此文对这个问题进行详细的讲解,力求让看完这篇文章的朋友在看完以后对于分页显示的原理和实现方法有所了解。本文适合初学者阅读,所有示例代码均使用php编写。
2、原理
所谓分页显示,也就是将数据库中的结果集人为的分成一段一段的来显示,这里需要两个初始的参数:
每页多少条记录($PageSize)?
当前是第几页($CurrentPageID)?
现在只要再给我一个结果集,我就可以显示某段特定的结果出来。
至于其他的参数,比如:上一页($PreviousPageID)、下一页($NextPageID)、总页数($numPages)等等,都可以根据前边这几个东西得到。
以mysql数据库为例,如果要从表内截取某段内容,sql语句可以用:select * from table limit offset, rows。看看下面一组sql语句,尝试一下发现其中的规率。
前10条记录:select * from table limit 0,10
第11至20条记录:select * from table limit 10,10
第21至30条记录:select * from table limit 20,10
……
这一组sql语句其实就是当$PageSize=10的时候取表内每一页数据的sql语句,我们可以总结出这样一个模板:
select * from table limit ($CurrentPageID - 1) * $PageSize, $PageSize
拿这个模板代入对应的值和上边那一组sql语句对照一下看看是不是那么回事。搞定了最重要的如何获取数据的问题以后,剩下的就仅仅是传递参数,构造合适的sql语句然后使用php从数据库内获取数据并显示了。以下我将用具体代码加以说明。
3、简单代码
请详细阅读以下代码,自己调试运行一次,最好把它修改一次,加上自己的功能,比如搜索等等。
[code:1:c1661dd3ea]
// 建立数据库连接
$link = mysql_connect("localhost", "mysql_user", "mysql_password")
or die("Could not connect: " . mysql_error());
// 获取当前页数
if( isset($_GET['page']) ){
$page = intval( $_GET['page'] );
}
else{
$page = 1;
}
// 每页数量
$PageSize = 10;
// 获取总数据量
$sql = "select count(*) as amount from table";
$result = mysql_query($sql);
$row = mysql_fetch_row($result);
$amount = $row['amount'];
// 记算总共有多少页
if( $amount ){
if( $amount < $page_size ){ $page_count = 1; } //如果总数据量小于$PageSize,那么只有一页
if( $amount % $page_size ){ //取总数据量除以每页数的余数
$page_count = (int)($amount / $page_size) + 1; //如果有余数,则页数等于总数据量除以每页数的结果取整再加一
}else{
$page_count = $amount / $page_size; //如果没有余数,则页数等于总数据量除以每页数的结果
}
}
else{
$page_count = 0;
}
// 翻页链接
$page_string = '';
if( $page == 1 ){
$page_string .= '第一页|上一页|';
}
else{
$page_string .= '第一页|上一页|';
}
if( ($page == $page_count) || ($page_count == 0) ){
$page_string .= '下一页|尾页';
}
else{
$page_string .= '下一页|尾页';
}
// 获取数据,以二维数组格式返回结果
if( $amount ){
$sql = "select * from table order by id desc limit ". ($page-1)*$page_size .", $page_size";
$result = mysql_query($sql);
while ( $row = mysql_fetch_row($result) ){
$rowset[] = $row;
}
}else{
$rowset = array();
}
// 没有包含显示结果的代码,那不在讨论范围,只要用foreach就可以很简单的用得到的二维数组来显示结果
?>
[/code:1:c1661dd3ea]
4、OO风格代码
以下代码中的数据库连接是使用的pear db类进行处理
[code:1:c1661dd3ea]
// FileName: Pager.class.php
// 分页类,这个类仅仅用于处理数据结构,不负责处理显示的工作
クラス ページャー
{
var $PageSize; //各ページの番号
var $PreviousPageID; // 前のページ
/ /合計ページ数
var $numItems; //最後のページです
var $sql; // SQL クエリ文
function Pager($option)
{
グローバル $d b;
$this->setOptions($option)
$res = $db->query($this->sql); >
if ( $this->numItems > 0 )
{
numPages = ceil($this->numItems / $this->PageSize);
}
他
{
$this->numPages = 0;
}
スイッチ ( $this->CurrentPageID )
>numPages == 1:
$this ->isFirstPage = true;
isLastPage = true;
$this-> isLastPage = false; 🎜> ブレーク;
case $this->numPages:
$this->isLastPage = true;
if($ this-&gt; numpages&gt; 1)
if ( !$this->isLastPage ) { $this->NextPageID = $this->CurrentPageID + 1; }
if ( !$this->isFirstPage ) { $this->前のページID = $this->現在のページID - 1; }
}
trueを返します。
}
/***
*
* 結果セットのデータベース接続を返します
* 結果セットが比較的大きい場合、このメソッドを直接使用してデータベース接続を取得し、クラスの外部を横断することができます。オーバーヘッドが少ない
* 結果セットがそれほど大きくない場合は、getPageData を直接使用して 2 次元配列形式で結果を取得できます
* getPageData メソッドも呼び出して結果を取得します
*
***/
function getDataLink()
{
if ( $this->numItems )
{
グローバル $db;
$PageID = $this->CurrentPageID;
$from = ($PageID - 1)*$this->PageSize;
$count = $this->PageSize;
$link = $db->limitQuery($this->sql, $from, $count); //使用Pear DB::limitQueryメソッド保证データ库互換性
return $link;
}
else
{
falseを返します。
}
}
/***
*
* 結果セットを 2 次元配列の形式で返します
*
***/
function getPageData()
{
if ( $this->numItems )
{
if ( $res = $this->getDataLink() )
{
if ( $res->numRows()
) {
while ( $row = $ res->fetchRow() )
{
$result[] = $row;
}
}
else
{
$result =配列();
}
return $result;
}
else
{
return false;
}
}
else
{
return false;
}
}
function _setOptions($option)
{
$allow_options = array(
'PageSize',
'CurrentPageID',
'sql',
'numItems'
);
foreach ( $option as $key => $value )
{
if ( in_array($key, $allow_options) && ($value != null) )
{
$this->$key = $value;
}
}
return true;
}
}
?>
// FileName: test_pager.php
// 这是一段简单的示例代码,前边省略了使用pear db类建立数据库连接的代码
require "Pager.class.php";
if ( isset($_GET['page']) )
{
$page = (int)$_GET['page'];
}
else
{
$page = 1;
}
$sql = "select * from table order by id";
$pager_option = array(
"sql" => $sql,
"PageSize" => 10,
"CurrentPageID" => $page
);
if ( isset($_GET['numItems']) )
{
$pager_option['numItems'] = (int)$_GET['numItems'];
}
$pager = @new Pager($pager_option);
$data = $pager->getPageData();
if ( $pager->isFirstPage )
{
$turnover = "首页|上一页|";
}
else
{
$turnover = "首页|上一页|";
}
if ( $pager->isLastPage )
{
$turnover .= "下一页|尾页";
}
else
{
$turnover .= "下一页|尾页";
}
?>
[/code:1:c1661dd3ea]
需要说明的地方有两个:
这个类仅仅处理数据,并不负责处理显示,因为我觉得将数据的处理和结果的显示都放到一个类里边实在是有些勉强。显示的时候情况和要求多变,不如自己根据类给出的结果处理,更好的方法是根据这个Pager类继承一个自己的子类来显示不同的分页,比如显示用户分页列表可以:
[code:1:c1661dd3ea]
Class MemberPager extends Pager
{
function showMemberList()
{
global $db;
$data = $this->getPageData();
// 显示结果的代码
// ......
}
}
/// 调用
if ( isset($_GET['page']) )
{
$page = (int)$_GET['page'];
}
else
{
$page = 1;
}
$sql = "select * from members order by id";
$pager_option = array(
"sql" => $sql,
"PageSize" => 10,
"CurrentPageID" => $page
);
if ( isset($_GET['numItems']) )
{
$pager_option['numItems'] = (int)$_GET['numItems'];
}
$pager = @new MemberPager($pager_option);
$pager->showMemberList();
?>
[/code:1:c1661dd3ea]
第二个需要说明的地方就是不同数据库的兼容性,在不同的数据库里截获一段结果的写法是不一样的。
mysql: select * from table limit offset, rows
pgsql: select * from table limit m offset n
......
所以要在类里边获取结果的时候需要使用pear db类的limitQuery方法。
ok,写完收功,希望花时间看完这些文字的你不觉得是浪费了时间。
【发表回复】【查看论坛原帖】【添加到收藏夹】【关闭】
--------------------------------------------------------------------------------
tonera 回复于:2003-10-16 12:14:48
加精华,这里的精华也太少了点。 :lol:
--------------------------------------------------------------------------------
I_Just_Shot_John_Lennon 回复于:2003-10-22 14:54:15
感谢夜猫子老大能把这么好的代码共享出来
刚好做一个分页的小程序,用的也是pear的db库
所以就想用上这段代码了
不过发现有个小地方需要改进一下
[code:1:1dff2b643d]
// 总条数
if ( !isset($this->numItems) )
{
$res = $db->query($this->sql);
$this->numItems = $res->numRows();
}
[/code:1:1dff2b643d]
就是再查询总条数的
用
select count(*) from table
比
select * from table来数条数应该效率上会好一些吧
对于数据量很大的数据表来说
--------------------------------------------------------------------------------
夜猫子 回复于:2003-10-22 15:04:52
是的,关于查询总条数的确有这个问题,不过完全可以用其他方式来搞定。
首先,如果采用select count(*) from table的话,我们就需要多传递一条sql语句,这可能使程序的可读性降低。
第二,其实在第一次调用以后,我们都可以把$pager->numItems这个总记录数通过翻页的那个链接传递给下一次。
[code:1:3262bf44d3]
if ( $pager->isFirstPage )
{
$turnover = "首页|上一页|";
}
else
{
$turnover = "首页|上一页|";
}
if ( $pager->isLastPage )
{
$turnover .= "下一页|尾页";
}
else
{
$turnover .= "下一页|尾页";
}
[/code:1:3262bf44d3]
然后我们只要在程序里边这么写就可以了:
[code:1:3262bf44d3]
$pager_option = array(
"sql" => $sql,
"PageSize" => 10,
"CurrentPageID" => $page
);
if ( isset($_GET['numItems']) )
{
$pager_option['numItems'] = (int)$_GET['numItems'];
}
$pager = new Pager($pager_option);
[/code:1:3262bf44d3]
这段代码在我文章里的示例里也有体现,这样处理以后,除了第一次需要去记算总条数以外,以后的翻页都直接使用上一次查询出来的数据,这样效率得到了提高。
--------------------------------------------------------------------------------
tonera 回复于:2003-10-27 10:32:05
要是我想实现每页显示指定数量的页码,怎么办?
例如:
有1000个记录,每页显示10条。
当我看到第5页时,显示的是51-60条记录。
同时我想让它显示页码:1 2 3 4 6 7 8 9
当我翻到第10页时,显示:
6 7 8 9 11 12 13 14
----------------------------------------------- --- ----------------------------------
NightKids さんの返信日: 2003-10-27 10 :47:10
私も楽しみに参加しています~~~私のページネーションクラス~~~
[code:1:cb1d006774]
ob_start(); (c) 2003 NightKids <
weidewang@magus-soft.com
> * の変更は、以下の条件を満たす場合に許可されます 著作権* 注意、この条件リストおよび以下の免責事項。 > * 2. バイナリ形式で再配布する場合は、上記の著作権
* 通知、この条件リストおよび以下の免責条項を
* ドキュメントおよび/または配布物に付属するその他の資料に記載する必要があります。
* このソフトウェア著者および寄稿者によって「現状のまま」提供され、
* 特定の目的を含むがこれに限定されない、いかなる明示的または黙示的な保証も行われません
* は、いかなる場合も、著者または寄稿者は免責されません。
* あらゆる直接的、間接的、付随的、特別、例示的、または結果的損害に対する責任
* 損害 (代替品
* またはサービスの購入を含むがこれらに限定されない、使用、データ、またはサービスの損失を含む)利益、または業務の中断)
* 原因の如何を問わず、また、契約上、厳格であるかを問わず、責任の理論に基づくもの
* 何らかの形で生じた責任または不法行為(過失またはその他を含む)
* 使用範囲外
* ような損害
*/ ob_gzhandler ");
//set_time_limit(10);
/********* の可能性がある場合でも、このソフトウェアを使用しないでください。 *************************** ********************** **************************** ********************* ******
* Black Wind が作成した PHP ファイル
* E-Mail:o5499@hotmail.com ;o5499@etang.com
* ホーム:
* QQ:9400355, 30148292
* 作成者: Black Wind
*
*
*
* 最終変更時刻:
*
* ステートメント: どの個人またはグループもコードを変更および配布できます。任意の場所で使用することができます(生じた損失や紛争は私とは何の関係もありません)。
* 何か良い提案やコメントがありましたら、私に連絡してください。
* このコメントを使用または配布する場合は、このコメントを保存し、あなたの労力の成果を尊重してください。ありがとう!!
*
* 周りの友達、そして私をサポートしてくれるすべての人に感謝します。
* 所有无私提供资料的网友和朋友们。所有 PHP 支持者 !
*
*
* 开发环境:Apache/1.3.27+PHP 4.3.0RC4+Zend Engine v1.3.0+MySQL 4.0.4-beta-max-nt+Zend Optimizer v2.0.3
*
* 要求:
*************************************************************************************************************/
//_SERVER["HTTP_REFERER"]; //http://black-server/Studio/
//_SERVER["QUERY_STRING"]; //返回问号后面的字符串
//_GET["变量名"]; //可以取回传入的一个变量
//_SERVER["REQUEST_URI"];//返回 /Studio/php_info.php?pp=阿嗄&kl=00&uj=90l
//_SERVER["argv"];以数组形式返回传入的值
//_SERVER["SCRIPT_NAME"];
//----------- The File The Start --------
//require_once("");
//include_once("");
/*
本类没有提供连接数据库的功能,所以需在外部打开相应的数据库。
本类也没有提供显示记录的功能,只是分页读取记录至 Result二维数组中。
需在外部自定义数据显示格式。
$tp = new TViewPage();
$tp->SetSQLBuff($sqlbuff);
$tp->SetPageQuery("action","NULL");
$row=$tp->ReadList();
$rowcount=count($row)-1;
$ThePage=$tp->ThePage();
$Page=$tp->Page1();
*/
class TViewPage
{
//var $dbc; //数据库链接
var $MaxLine=10; //每页显示行数
var $Offset; //记录偏移量
var $Total=0; //记录总数
var $Number; //本页读取的记录数
var $Result; //读出的结果
var $TPages=0; //总页数
var $CPages; //当前页数
var $Condition; //显示条件 如:where id='$id' order by id desc
var $PageQuery; //分页显示要传递的参数
var $column; //显示的列
var $ParKey;
var $debug=false;
var $_sqlbuff=null;
var $php_self;
var $vpdispnum=8;//分页条数字显示个数 样式 3 使用
/*
NightKids
2003-10-20 15:04:59 添加 wap 版本分页
**/
var $iswap=false;
//******构造函数*************
//参数:表名、最大行数、偏移量
function TViewPage($ML=10)
{
global $offset;
$offset=$_GET[offset];
$this->MaxLine=$ML;
$this->column=$column;
if(isset($offset)) $this->Offset=intval($offset);
else $this->Offset=0;
$this->php_self=$_SERVER["SCRIPT_NAME"];
}
function SetSQLBuff($sqlbuff) //使用外部的 Sql 指令
{ $this->_sqlbuff=$sqlbuff;}
//********设置显示条件*********
//如:where id='$id' order by id desc
//要求是字串,符合SQL语法(本字串将加在SQL语句后)
//******设置传递参数************
// key参数名 value参数值 页面参数
// 如:setpagequery("id",$id);如有多个参数要传递,可多次调用本函数。
function SetPageQuery($key,$value)
{
$tmp[key]=$key;
$tmp[value]=$value;
$this->PageQuery[]=$tmp;
}
//********读取记录***************
// 主要工作函数,根据所给的条件从表中读取相应的记录
// 返回值是一个二维数组,Result[记录号][字段名]
function ReadList()
{
$col=$this->column;
if($this->Total==0)
{
$sqlbuff=$this->_sqlbuff;
$result=@mysql_query($sqlbuff) or die(mysql_error());
$this->Total=@mysql_num_rows($result);
@mysql_free_result($result);
}
if($this->debug)
echo "
".$sqlbuff."
";
if($this->Total>0)
{ //根据条件 Condition
$SQL=$this->_sqlbuff." LIMIT ".$this->Offset." , ".$this->MaxLine;
$result=@mysql_query($SQL) or die(mysql_error());
$this->Number=@mysql_num_rows($result);
while($row=@mysql_fetch_Array($result))
{ $this->Result[]=$row; }
@mysql_free_result($result);
}
return $this->Result;
}
//**********显示页数*************
//显示当前页及总页数
function ThePage()
{
$TmpStr="总数 ".$this->TotalNum()." ";
return $TmpStr." 第".$this->CurrentPage()."页/共".$this->TotalPages()."页";
}
function TotalNum() //总纪录数
{
return $this->Total;
}
function TotalPages() //总页数
{
return $this->TPages=ceil($this->Total/$this->MaxLine);
}
function CurrentPage() //当前页
{
return $this->CPages=$this->Offset/$this->MaxLine+1;
}
function PageQueryStr() //生成一个要传递参数字串
{
$k=count($this->PageQuery);
$strQuery=""; //生成一个要传递参数字串
$LinkChar="&";
if($this->iswap) $LinkChar="&";
for($i=0;$i<$k;$i++){
$strQuery.="$LinkChar".$this->PageQuery[$i][key]."=".$this->PageQuery[$i][value];
}
return $strQuery;
}
function FirstPageOffset() //第一页的偏移
{ return 0;}
function NextPageOffset() //下一页的偏移
{ return $this->Offset+$this->MaxLine;}
function PrevPageOffset() //上一页的偏移
{ return $this->Offset-$this->MaxLine;}
function LastPageOffset() //最后一页的偏移
{ return ($this->TotalPages()-1)*$this->MaxLine;}
//**********显示翻页按钮*************
//此函数要在ThePage()函数之后调用!!!
//显示首页、下页、上页、未页,并加上要传递的参数
function Page($style=null) //翻页样式1
{
if(!empty($style))
$style=" style=\"".$style."\"";
$first=$this->FirstPageOffset();
$next=$this->NextPageOffset();
$prev=$this->PrevPageOffset();
$last=$this->LastPageOffset();
$strQuery=$this->PageQueryStr();
$LinkTmpStr=null;
$php_self=$this->php_self;
if($this->iswap==false){ //不是 wap 版本
if($this->Offset>=$this->MaxLine)
{ $LinkTmpStr.="首页 | ";}
if($prev>=0)
{$LinkTmpStr.="上一页 | "; }
if($next<$this->Total)
{$LinkTmpStr.="下一页 | "; }
if($this->TPages!=0 && $this->CPages<$this->TPages)
{$LinkTmpStr.="末页"; }
return $LinkTmpStr="".$LinkTmpStr."";
}else{
/*
if($this->Offset>=$this->MaxLine)
{ $LinkTmpStr.="首页
";}
*/
if($prev>=0)
{$LinkTmpStr.="上一页
"; }
if($next<$this->Total)
{$LinkTmpStr.="下一页
"; }
/*
if( ($this->TPages!=0) && ($this->CPages<$this->TPages) && ($this->TPages>1) )
{$LinkTmpStr.="末页
"; }
*/
return $LinkTmpStr=$LinkTmpStr;
}
}
function Page1($style=null) // 翻页样式 2
{
if(!empty($style))
$style=" .$style."" ";
$first=$this->FirstPageOffset();
$next=$this->NextPageOffset();
$prev=$this->PrevPageOffset();
$last=$this->LastPageOffset();
$strQuery=$this->PageQueryStr();
$php_self=$this->php_self;
$FirstPageStr=" 首页 | ";
$NextPageStr="下一页< ;/a>";
$PrevPageStr="上一页 | ";
$LastPageStr=" 末页";
$LinkTmpStr=null;
$offset=$this->オフセット;
$LinkTmpStr=null;
$CurrentPage=$this->CurrentPage();
$LastPage=$this->TotalPages();
if($this->TotalPages() <=1 ) return;
if($CurrentPage == 1)
{ return " 首页 | 上一页 | ".$NextPageStr.$LastPageStr; }
if($CurrentPage==$LastPage)
{ return $FirstPageStr.$PrevPageStr."下一页 | 末页 ";
return $FirstPageStr.$PrevPageStr.$NextPageStr.$LastPageStr;
}
function Page2($style=null) //样式3
{
if(!empty($style))
$style=" . $style.""";
$TotalPage=$this->TotalPages();
$CurrentPage=$this->CurrentPage();
$TmpStr="
$TmpStr を返す;
}
function Page3($style=null) //样式4
{
if(!empty($style))
$style=" . $style.""";
$CurrentPage=$this->CurrentPage();
$boxsize=strlen($CurrentPage);
if($boxsize==1) $boxsize=1;
else $boxsize-=1;
$TmpStr="";
$TmpStr.="php_self."?offset='+((x99tvpbox. value-1)*".$this->MaxLine.")+'".$this->PageQueryStr()."';this.disabled=true;x99tvpbox.disabled=true" $style>n";
$TmpStr を返します;
}
//*********************************クラス終了
}
//--------- -- ファイル 終わり ----------
//如意见または问题请致信到 o5499@hotmail.com;o5499@etang.com
/*
$tp = new TViewPage();
$tp->SetSQLBuff($sqlbuff);
$tp->SetPageQuery("アクション","NULL");
$row=$tp->ReadList();
$rowcount=count($row)-1;
$ThePage=$tp->ThePage();
$Page=$tp->Page1();
使用例:
function 李子(){
$tp = new TViewPage();
$sqlbuff="SELECT * FROM `test` WHERE `id`>100 ";
$tp->SetSQLBuff($sqlbuff);
$tp->SetPageQuery("アクション","NULL");
$row=$tp->ReadList();
$rownum=count($row);
#################
$tx = new z99Template();
$tx->SetVar("SelfPage",$_SERVER["SCRIPT_NAME"]);
$tx->SetFile("./template/");
$tx->SetBlock("BlockRomName");
###################
for($i=0;$i<=$rownum-1;$i++)
{
$tx->SetVar("",$row[$i][id]);
$tx->AddToBlock("BlockRomName");
}
$ThePage=$tp->ThePage();
$Page=$tp->Page1();
return $tx->OnlyReturn();
}
*/
ob_end_flush();
?>