Zend_Paginator分页代码

Standard

Models
Book.php

class Book extends Zend_Db_Table {
  ...
}

Controllers
IndexController.php

class IndexController extends Zend_Controller_Action {
 
  function init() {
    $this->initView();
    //set baseUrl
    $this->view->baseUrl = $this->_request->getBaseUrl();
  }
 
  function indexAction() {
    $this->view->title = "Title";
    $book = new Book();
    $this->view->books = $book->index();
 
    $pageNumber = 2;
    $paginator = Zend_Paginator::factory($this->view->books);
    $paginator->setItemCountPerPage($pageNumber);
    $paginator->setCurrentPageNumber($this->_getParam('page'));
    Zend_Paginator::setDefaultScrollingStyle('Sliding');
    $paginator->setView($this->view);
 
    $this->view->books = $paginator;
 
    $this->view->paginator = $paginator;
 
    $this->render();
  }
}

Views
views\scripts\index\index.phtml

<?php foreach ($this->books as $item) {?>
<ul id='ul_<?php echo $this->escape($item->id) ?>'>
  <li>
  <table width="100%"><tr><td width="60%">[<?php echo $this->escape($item->username) ?>]</b> Create by:<?php echo $this->escape($item->pubdate) ?></td><td align="right"><a href="<?php echo $this->baseUrl ?>/index/add/id/<?php echo $item->id ?>?height=280&width=280" class="thickbox" title="Edit">EDIT</a> <a href="#" id="del_<?php echo $this->escape($item->id) ?>" class="del">DEL</a></td></tr></table>
  </li>
  <li class="bottom"><?php echo nl2br($this->escape($item->content)) ?></li>
</ul>
<?php } ?>
<ul>
  <li>
  <?php echo $this->paginationControl($this->paginator, 'Elastic', 'pagelist.phtml'); ?>
  </li>
</ul>

views\scripts\pagelist.phtml

<?php if ($this->pageCount): ?>
<!-- yemaosheng.com -->
<div class="paginationControl">
<!-- Previous page link -->
<?php if (isset($this->previous)): ?>
   <a href="<?php echo $this->url(array('controller'=>'index', 'action'=>'index', 'page'=>$this->previous)); ?>">< 上一页</a> |
<?php else: ?>
   <span class="disabled">< 上一页</span> |
<?php endif; ?>
<!-- Numbered page links -->
<?php foreach ($this->pagesInRange as $page): ?>
  <?php if ($page != $this->current): ?>
     <a href="<?php echo $this->url(array('controller'=>'index', 'action'=>'index', 'page'=>$page)); ?>"><?= $page; ?></a> |
  <?php else: ?>
    <?= $page; ?> |
  <?php endif; ?>
<?php endforeach; ?>
<!-- Next page link -->
<?php if (isset($this->next)): ?>
   <a href="<?php echo $this->url(array('controller'=>'index', 'action'=>'index', 'page'=>$this->next)); ?>">下一页 ></a>
<?php else: ?>
   <span class="disabled">下一页 ></span>
<?php endif; ?>
</div>
<?php endif; ?>

转载请写明出处: http://yemaosheng.com

高效的MySQL分页

Standard

转载自 超群.com的博客

PERCONA PERFORMANCE CONFERENCE 2009上,来自雅虎的几位工程师带来了一篇”Efficient Pagination Using MySQL“的报告,有很多亮点,本文是在原文基础上的进一步延伸。

首先看一下分页的基本原理:

mysql> EXPLAIN SELECT * FROM message ORDER BY id DESC LIMIT 10000, 20\G
***************** 1. ROW **************
id: 1
select_type: SIMPLE
TABLE: message
TYPE: INDEX
possible_keys: NULL
KEY: PRIMARY
key_len: 4
REF: NULL
ROWS: 10020
Extra:
1 ROW IN SET (0.00 sec)

limit 10000,20的意思扫描满足条件的10020行,扔掉前面的10000行,返回最后的20行,问题就在这里,如果是limit 100000,100,需要扫描100100行,在一个高并发的应用里,每次查询需要扫描超过10W行,性能肯定大打折扣。文中还提到limit n性能是没问题的,因为只扫描n行。

文中提到一种”clue”的做法,给翻页提供一些”线索”,比如还是SELECT * FROM message ORDER BY id DESC,按id降序分页,每页20条,当前是第10页,当前页条目id最大的是9527,最小的是9500,如果我们只提供”上一页”、”下一页”这样的跳转(不提供到第N页的跳转),那么在处理”上一页”的时候SQL语句可以是:

SELECT * FROM message WHERE id > 9527 ORDER BY id ASC LIMIT 20;

处理”下一页”的时候SQL语句可以是:

SELECT * FROM message WHERE id < 9500 ORDER BY id DESC LIMIT 20;

不管翻多少页,每次查询只扫描20行。

缺点是只能提供”上一页”、”下一页”的链接形式,但是我们的产品经理非常喜欢”<上一页 1 2 3 4 5 6 7 8 9 下一页>”这样的链接方式,怎么办呢?

如果LIMIT m,n不可避免的话,要优化效率,只有尽可能的让m小一下,我们扩展前面的”clue”做法,还是SELECT * FROM message ORDER BY id DESC,按id降序分页,每页20条,当前是第10页,当前页条目id最大的是9527,最小的是9500,比如要跳到第8页,我看的SQL语句可以这样写:

SELECT * FROM message WHERE id > 9527 ORDER BY id ASC LIMIT 20,20;

跳转到第13页:

SELECT * FROM message WHERE id < 9500 ORDER BY id DESC LIMIT 40,20;

原理还是一样,记录住当前页id的最大值和最小值,计算跳转页面和当前页相对偏移,由于页面相近,这个偏移量不会很大,这样的话m值相对较小,大大减少扫描的行数。其实传统的limit m,n,相对的偏移一直是第一页,这样的话越翻到后面,效率越差,而上面给出的方法就没有这样的问题。

注意SQL语句里面的ASC和DESC,如果是ASC取出来的结果,显示的时候记得倒置一下。

已在60W数据总量的表中测试,效果非常明显。

PHP分页函数

Standard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
if(!function_exists(pageft)){
function pageft($totle,$displaypg=20,$url=''){
global $page,$firstcount,$pagenav,$_SERVER;
$GLOBALS["displaypg"]=$displaypg;
if(!$page) { $page=1; }
if(!$url){
$url=$_SERVER["REQUEST_URI"];
}
//URL分析:
$parse_url=parse_url($url);
$url_query=$parse_url["query"]; //单独取出URL的查询字串
if($url_query){
$url_query=ereg_replace("(^|&)page=$page","",$url_query);
//将处理后的URL的查询字串替换原来的URL的查询字串:
$url=str_replace($parse_url["query"],$url_query,$url);
//在URL后加page查询信息,但待赋值:
if($url_query){
$url.="&page";
}else{
$url.="page";
}
}else {
$url.="?page";
}
$lastpg=ceil($totle/$displaypg); //最后页,也是总页数
$page=min($lastpg,$page);
$prepg=$page-1; //上一页
$nextpg=($page==$lastpg ? 0 : $page+1); //下一页
$firstcount=($page-1)*$displaypg;
//开始分页导航条代码:
$pagenav="显示第 <B>".($totle?($firstcount+1):0)."</B>-<B>".min($firstcount+$displaypg,$totle)."</B> 条记录,共 $totle 条记录<BR>";
//如果只有一页则跳出函数:
if($lastpg<=1) return false;
$pagenav.=" <a href='$url=1'>首页</a> ";
if($prepg){
$pagenav.=" <a href='$url=$prepg'>前页</a> ";
}else{
$pagenav.=" 前页 ";
}
if($nextpg){
$pagenav.=" <a href='$url=$nextpg'>后页</a> ";
}else{
$pagenav.=" 后页 ";
}
$pagenav.=" <a href='$url=$lastpg'>尾页</a> ";
//下拉跳转列表,循环列出所有页码:
$pagenav.=" 到第 <select name='topage' size='1' onchange='window.location=\"$url=\"+this.value'>\n";
for($i=1;$i<=$lastpg;$i++){
if($i==$page){
$pagenav.="<option value='$i' selected>$i</option>\n";
}else{
$pagenav.="<option value='$i'>$i</option>\n";
}
}
$pagenav.="</select> 页,共 $lastpg 页";
}
}
 
$sql="SELECT * FROM tb";
//取得总信息数
$result=mysql_query($sql);
$total=mysql_num_rows($result);
//调用pageft(),每页显示10条信息(使用默认的20时,可以省略此参数),使用本页URL(默认,所以省略掉)。
pageft($total,10,"http://www.yemaosheng.com/index");
//现在产生的全局变量就派上用场了:
$result=mysql_query($sql." limit $firstcount,$displaypg");
$i=1;
while($row=mysql_fetch_array($result)){
$array[]=array("index"=>$i,"date"=>$row['date'],"type"=>$row['type'],"value"=>$row['value']);
$i++;
}
$pagenav;//分页导航条代码
$main_smarty->assign('mainlist', $array);
$main_smarty->assign('pagenum', $pagenav);
$main_smarty->display('x.tpl');
 
----------------smarty template x.tpl----------------
<table>
{section name=sec1 loop=$mainlist}
<tr>
<td width="76"><strong>{$mainlist[sec1].index}</strong></td>
<td width="138">{$mainlist[sec1].date}</td>
<td width="274">{$mainlist[sec1].type}</td>
<td width="114">{$mainlist[sec1].value}</td>
</tr>
{/section}
</table>
<center>{$pagenum}</center>