Zend_Paginator について

Zend Framework1.5x 以前では ページングは自前で実装するか、 PEAR::Pager を使用すると実現したが*1、1.6 から Zend_Paginator でページングを実現するコンポーネントが追加された。


Zend_Paginator には Adapter がいくつかあって、Zend_Paginator_Adapter_Array の使い方は Zend_Paginatorを使ってみた - 気が向いたら が詳しい。


Zend_Paginator_Adapter_Array だとデータベースのデータ全件を取得してページングをするので、件数が多くなると、パフォーマンスが劣化するんじゃないかと思った。
ドキュメントを読んでみると、

注意
指定したクエリにマッチするすべての行を取得するのではなく、 DbSelect アダプタや DbTableSelect アダプタは 現在のページの表示のための必要最小限のデータのみを取得します。
そのため、マッチする行の総数を得るための別のクエリが動的に生成されます。 しかし、総数を直接指定したり、総数を求めるクエリを直接指定したりすることもできます。 詳細な情報は、DbSelect アダプタの setRowCount() メソッドを参照ください。

http://framework.zend.com/manual/ja/zend.paginator.usage.html

Zend_Paginator_Adapter_DbSelect や Zend_Paginator_Adapter_DbTableSelect を使えばその心配はなさそう。


じゃあ Zend_Db_Select や Zend_Db_Table_Select を使わずに、Zend_Db_Statement を使用してデータを取得している場合はどうすれば良いのか。
その場合は、 Zend_Paginator_Adapter_Null を使用すれば実現出来た。

<?php
$limt = 10;
// Zend_Paginator の開始値が 1 だけど、Db の offset は 0 始まりのため
$offset = (intval($this->_getParam('page')) - 1) * $limit;
// Zend_Db_Statement で offset, limit を使用して pageId に該当するデータを取得し、
// $rows には配列で結果が入ってるとする
$rows = $dao->getData($id, $offset, $limit);
// データの総件数を取得する。
$count = $dao->getCount($id);

$adapter     = new Zend_Paginator_Adapter_Null($count);
$paginator = new Zend_Paginator($adapter);
$paginator->setCurrentPageNumber($this->_getParam('page'))
                   ->setItemCountPerPage($limit);
Zend_Paginator::setDefaultScrollingStyle('Sliding');
Zend_View_Helper_PaginationControl::setDefaultViewPartial('my_pagination_control.phtml');

// Zend_Paginator_Adapter_Null を使用する場合は、自前で view に assign する必要がある。
$this->view->assign('list', $rows);


Zend_Paginator_Adapter_Array と Zend_Paginator_Adapter_Null とパフォーマンスに差があるのか簡単に計測してみた。
データ件数が 100 件で、1 ページに 10 件表示する。
ブートストラップの最初で計測開始、ディスパッチが終わった後に計測終了とした。

ページNo Adapter_Array Adapter_Null(*1) Adapter_Null(*2)
page=1 0.115587949753 0.13925409317 0.114986896515
page=2 0.111268043518 0.142893075943 0.117069959641
page=3 0.112726926804 0.136443138123 0.12135887146
page=4 0.112685918808 0.140572786331 0.117425203323
page=5 0.112499952316 0.145317077637 0.116499185562
page=6 0.113018989563 0.160613059998 0.116165161133
page=7 0.111841201782 0.138566970825 0.121479988098
page=8 0.113499164581 0.135857105255 0.117434978485
page=9 0.111684083939 0.136906147003 0.117306947708
page=10 0.112828016281 0.144604921341 0.118587017059

(*1) 総件数を Zend_Db_Select_Table を使用して取得した場合
(*2) 総件数を Zend_Db_Statement を使用して取得した場合


Zend_Paginator_Adapter_Null の方が遅くなっとる…
総件数を取得する SQL を投げている分遅くなってるのか。
しかも Zend_Db_Select_Table を使用して取得した方が更に遅い。


データ件数が多くなれば結果は変わるだろうと思うけど、大量のデータを投入する気力がなくなったw
それと 単純に件数を取得するだけなら、 Zend_Db_Statement を使う方が速い。