PHP多库多表分页的实现

      Web 2006-1-12 11:42

  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;
  }
 }

  其实,这段代码只是实现了多库多表分页的功能,算法的效率还比较低,每次翻页都需要遍历两次数据库。对于数据量比较大的情况,比如纪录条数达到几十万条,就有的等了,甚至还有可能会导致页面超时。因此,这种方法还有待改进。

标签集:TAGS:
回复Comments() 点击Count()

回复Comments

{commenttime}{commentauthor}

{CommentUrl}
{commentcontent}