PHP中单库单表的分页直接用SQL的limit子句就可以很容易的实现,但是多库多表的分页就比较麻烦了。MySQL 4.x中好像还不支持视图,只好用程序来实现多库多表的分页了。
首先假设要从所有名字为DB*的数据库中取数据,并且所有数据库中的表结构都完全一样。这里假定从所有数据库的所有表中取数据。当然,这是一种简化的情况,实际情况下可能还需要根据特定条件来确定需要访问的数据库和表。
基本的思路还是蛮简单的,就是先获得数据库和表的列表,保存在数组中,然后开始遍历。每次记住当前页遍历到哪个数据库哪个表的哪一条纪录(也就是保存三个“指针”变量,分别纪录数据库的偏移、库内表的偏移以及表内纪录的偏移),将它们作为URL参数传递给下一页,再下一页中直接从这个地方继续遍历。这种方式可以方便的实现下一页的翻页,但是要实现上一页还是比较麻烦的,因为要计算上一页的那三个“指针”值很麻烦。(不过有一种偷懒的办法,就是直接用javascript的history.go(-1)来实现返回上一页)。但是这种方法存在一个更大的问题,就是不能实现任意页的翻页,只能一页一页的翻,不能直接跳到最后一页,或者指定一个任意的页码。于是,我又想到了一个改进的方法,就是不用每次传递三个“指针”参数,而直接把页码作为URL参数,根据页码来计算那三个“指针”的值。每次加载页面时,首先遍历所有数据库计算出总共的纪录条数,同时算出三个“指针”的值。接着根据三个“指针”的值再次遍历数据库,取出需要的数据。由于遍历数据库都需要三层循环(数据库循环、库内表循环、表内纪录循环),这种改进的方法虽然实现了任意翻页,但是由于加了一次遍历数据库的操作,因此也大大降低了执行的效率,可以说有得必有失吧。下面还是结合代码来看看具体的实现方法吧。
变量说明:
$ page 是通过URL传递的页码参数,页面加载时从URL参数种获得。
$ perpage 是每页显示的记录条数,可以指定为任意正整数。
$ db, $ tb, $ offset 分别是数据库偏移,库内的表偏移和表内的纪录偏移,这三个变量可以根据$ page和$ perpage计算出来。
$ pdatabase 是保存数据库列表的数组。
$ ptable 是保存库内表列表的数组。
$ totalrows 是所有满足条件的纪录总数。
$ flag 是退出循环标志。
$ condition 是表内数据查询条件。
实现代码:
// init variables, you can change these values
$ perpage = 20;
$ condition = '1 = 1';
// get database array
$ i = 0;
$ pdatabase = array();
$ databases = mysql_query("SHOW DATABASES LIKE 'DB%';");
while ($ database = mysql_fetch_array($ databases, MYSQL_NUM))
{
$ pdatabase[$ i++] = $ database[0];
}
mysql_free_result($ databases);
// get table array
$ i = 0;
$ ptable = array();
mysql_select_db($ pdatabase[0]);
$ tables = mysql_query("SHOW TABLES FROM $ pdatabase[0]");
while ($ table = mysql_fetch_array($ tables, MYSQL_NUM))
{
$ ptable[$ i++] = $ table[0];
}
mysql_free_result($ tables);
// sanitize page param
if ($ page < 1)
{
$ page = 1;
}
// calculate current start row
$ startrow = ($ page - 1) * $ perpage;
// database offset
$ db = 0;
// table offset
$ tb = 0;
// row offset
$ offset = 0;
// number of rows in all databases
$ totalrows = 0;
// number of rows in a certain table
$ tablerows = 0;
// flag
$ flag = true;
// calculate $ db, $ tb & $ offset according to $ page & $ perpage
// database loop
for ($ i = 0; $ i < count($ pdatabase); $ i++)
{
mysql_select_db($ pdatabase[$ i]);
// table loop
for ($ j = 0; $ j < count($ ptable); $ j++)
{
// construct sql
$ sql = "select count(id) from " . $ ptable[$ j] . " where " . $ condition;
// do the query
$ tablerows = mysql_result(mysql_query($ sql, MYSQL_NUM), 0);
if ($ flag)
{
if ($ totalrows + $ tablerows > $ startrow)
{
$ db = $ i;
$ tb = $ j;
$ offset = $ startrow - $ totalrows;
if ($ offset < 0)
{
$ offset = 0;
}
$ flag = false;
}
}
$ totalrows += $ tablerows;
}
}
// page count
$ pagecount = ceil($ totalrows / $ perpage);
if ($ page > $ pagecount)
{
$ page = 1;
$ db = 0;
$ tb = 0;
$ offset = 0;
}
// page nav
if ($ page > 1)
{
$ PreviousPage = "<a href=" . $ _SERVER["PHP_SELF"] . "?page=" . ($ page - 1) . ">上一页</a>";
$ FirstPage = "<a href=" . $ _SERVER["PHP_SELF"] . "?page=1>首页</a>";
}
else
{
$ PreviousPage = "上一页";
$ FirstPage = "首页";
}
if ($ page < $ pagecount)
{
$ NextPage = "<a href=" . $ _SERVER["PHP_SELF"] . "?page=" . ($ page + 1) . ">下一页</a>";
$ LastPage = "<a href=" . $ _SERVER["PHP_SELF"] . "?page=" . $ pagecount . ">末页</a>";
}
else
{
$ NextPage = "下一页";
$ LastPage = "末页";
}
$ flag = false;
// count of fetched rows
$ count = 0;
// database loop
for ($ i = $ db; $ i < count($ pdatabase); $ i++)
{
mysql_select_db($ pdatabase[$ i]);
// table loop
for ($ j = $ tb; $ j < count($ ptable); $ j++)
{
// construct sql
$ sql = "select * from " . $ ptable[$ j] . " where " . $ condition;
$ sql .= " limit $ offset, $ perpage";
// do the query
$ items = mysql_query($ sql);
// row loop
while ($ item = mysql_fetch_array($ items))
{
// display row
// your code goes here ...
// update count
$ count++;
// get out of loop
if ($ count >= $ perpage)
{
$ flag = true;
break;
}
}
// update offset
$ offset = 0;
// free result
mysql_free_result($ items);
// get out of loop
if ($ flag)
{
break;
}
}
// get out of loop
if ($ flag)
{
break;
}
}
其实,这段代码只是实现了多库多表分页的功能,算法的效率还比较低,每次翻页都需要遍历两次数据库。对于数据量比较大的情况,比如纪录条数达到几十万条,就有的等了,甚至还有可能会导致页面超时。因此,这种方法还有待改进。
回复Comments
{commenttime}{commentauthor}
{CommentUrl}
{commentcontent}