6.限制查询结果上面我们讨论了如何通过使用一个数据库库函数使应用程序更简洁,更易于移植。比如从MS SQL Server转移到MySQL,在MS SQL Server中使用指令“SELECT TOP 15 name FROM employee”取得数据的前15条,可在MySQL中却不支持这种写法,而要写成:SELECT name FROM employee LIMIT 15。 它似乎对我们敲响了警钟,应该停止在查询语句中使用非标准SQL指令,而去认真地学习标准的SQL。 幸运的是,ADODB有一个处理 LIMIT的方法:SelectLimit(),这样我们就根本不用管连接的是MySQL还是MS SQL Server,ADODB会在底层为我们自动转换,请见下面的脚本例子: Connect("localhost", "root", "passwd", "adodb") or die("Unable to connect!"); // 构造并执行一个查询 // 我们要取得5行记录,从符合记录的第3行开始取 $query = "SELECT * FROM library"; $result = $db->SelectLimit($query, 5, 3) or die("Error in query: $query. " . $db->ErrorMsg()); // 遍历记录集 while (!$result->EOF) { echo $result->fields[1] . " - " . $result->fields[2] . "\n"; $result->MoveNext(); } // 清理无用的对象 $db->Close(); ?> 在这个例子中,selectlimit()方法类似于MySQL的LIMIT语句,可用于控制从某行开始查询,到某行的结果,从而取得我们指定的记录集。 我们可以利用ADODB提供的MetaDatabases()方法取得当前服务器中所有数据库的清单。还有一个方法和它很类似,即使用MetaTables()方法可以取得当前库中所有表的清单。请看下面的例子: Connect("localhost", "root", "passwd", "adodb") or die("Unable to connect!"); // 取得数据列表 echo "数据库:\n"; foreach($db->MetaDatabases() as $d){ echo "* $d\n"; } // 取得数据表清单 echo "\n当前数据库下的表:\n"; foreach($db->MetaTables() as $table){ echo "* $table\n"; } // 清理无用的对象 $db->Close(); ?> 7.快速存取有时,我们需要对一些不同的值做一些特殊的查询,比如一系列的INSERT(插入)语句。ADODB类提供了两个方法,可以使我们既节约时间又节省系统的开销,请看如下示例: Connect("localhost", "root", "passwd", "adodb") or die("Unable to connect!"); // 构造准备查询,使用参数绑定 $query = $db->Prepare("INSERT INTO library (title, author) VALUES (?, ?)"); // 从CSV 中取得要插入的标题和作者名称 $data = file("./book_list.csv"); // 遍历该文件,并执行插入操作 foreach ($data as $l){ $arr = explode(",", $l); // 插入值并绑定准备语句 $result = $db->Execute($query, array($arr[0], $arr[1])) or die("Error in query: $query. " . $db->ErrorMsg()); } // 清理无用的对象 $db->Close; ?> prepare()函数,把一个SQL查询作为参数,读取一个查询,但并不立即执行。prepare()返回一个句柄给一个prepare查询,当保存和传递给Execute()方法后,则立即执行该查询。 8.处理事务处理事务是许多应用程序的一个重要的特征(比如,钱从你的账户转出,然后转入到某个人的账户中。只要其中任意一步操作失败,这整个过程都必须被认定为失败。不然,钱被划出,而没有进对方的账户;或者,钱没有划出,但对方账户无端多了一笔钱)。 处理事务可以在代码级上进行机警地管理控制。常数错误检查被用来判断执行COMMIT(事务的所有各项都正确,执行正确,结束事务)还是执行ROLLBACK(事务中有错误,所有改动需要恢复原来状况)。 现在的数据库系统绝大多数都支持事务,如MySQL、Oracle、MS SQL Server等,ADODB提供一个非常好的功能,能够让你更透明地使用这一特性。请看下面的例子: Connect("localhost", “root”, “root”, “adodb”) or die("Unable to connect!"); //关闭auto-commit自动提交事务 // 开始事务处理语句块 $db->BeginTrans(); // 第一次查询 $query = "INSERT INTO library (title, author) VALUES ('测试用书', '佚名')"; $result = $db->Execute($query) or die("Error in query: $query. " . $db->ErrorMsg()); //使用第一次查询返回的ID号 if ($result){ $id = $db->Insert_ID(); $query = "INSERT INTO purchase_info (id, price) VALUES ($id, 'RMB 31.9')"; $result = $db->Execute($query) or die("Error in query: $query. " . $db->ErrorMsg()); } // 如果操作成功 if ($result){ // 事务提交 $db->CommitTrans(); }// 否则回滚 else{ $db->RollbackTrans(); } // 清理无用的对象 $db->Close; ?> 该脚本首先需要关掉数据库的auto commit功能,通过begintrans()方法来处理,这种方法也标志着一个事务的开始。可以使用CommitTrans()或RollbackTrans()函数来处理操作,一旦auto commit已经关掉,你就可以任意执行所需要的查询,确认事务的查询执行无误并完毕后,由我们自己决定何时执行commit操作。 每一次执行Execute()事务块后,它会返回一个布尔值,告诉我们是否成功地执行了查询。可以跟踪到这个值,以及使用的时间,以决定是否要进行整个交易行为。一旦你相信一切都没问题,则告诉数据库committrans()方法;如果发现有错误发生,则可以进行回滚操作——执行rollbacktrans()方法。 值得注意的是,注意您的数据库类型是否支持这些事务函数,前面已经说过,MySQL的InnoDB类型表支持事务,但是MyISAM类型并不支持。 9.使用缓存查询在一个动态页面中,如果其中的一个查询指令很少改变且频繁被执行,我们则可以使用ADODB的缓存功能,可以将查询指令的结果缓存成静态文件,从而提高PHP脚本的性能。 当试图通过缓存来提高你的应用程序的性能之前,建议先去优化查询指令再开始本操作,这样才会起到事半功倍之效果。 ADODB最棒的功能就是提供查询缓存的功能。缓存可以大大改善应用程序的性能,尤其是网站系统,因为大部分用户都是在浏览网站,数据库完成的任务多半是查询(SELECT操作)。为了更好地理解与应用缓存查询的功能,我们来看下面的脚本例子。 Connect("localhost", "root", "root", "adodb") or die("Unable to connect!"); // 构造并执行一个查询 $query = "SELECT * FROM library"; $result = $db->Execute($query) or die("Error in query: $query. " . $db->ErrorMsg()); // 遍历返回的记录集,显示列数据的内容 TITLE 和 AUTHOR while (!$result->EOF) { echo $result->fields[1] . " - " . $result->fields[2] . "\n"; $result->MoveNext(); } // 显示取得的记录行数 echo "\n[" . $result->RecordCount() . " 行记录被返回]\n"; // 关闭数据库连接 $db->Close(); ?> 这段代码使用ADODB进行一个SELECT操作。比如说,这就是您的网站,平均有每分钟5000次的点击(PV,Page View)量,那么数据库系统每小时至少要被查询3万次以上,可以想象,这对我们的MySQL数据库的负载是相当繁重的。 因此ADODB提供了缓存的功能,可以将经常查询的结果保存起来,进而降低数据库服务器的负荷,同时也向用户提供更快速的内容响应。 下面是修改上面的脚本,改为使用CacheExecute来进行缓存查询的示例: Connect("localhost", "root", "passwd", "adodb") or die("Unable to connect!"); // 构造并执行一个查询 $query = "SELECT * FROM library"; $result = $db->CacheExecute(300,$query) or die("Error in query: $query. " . $db->ErrorMsg()); // 遍历返回的记录集,显示列数据的内容 TITLE 和 AUTHOR while (!$result->EOF) { echo $result->fields[1] . " - " . $result->fields[2] . "\n"; $result->MoveNext(); } // 取得和显示返回的记录行数 echo "\n[" . $result->RecordCount() . " 行记录被返回]\n"; // 关闭数据库连接 $db->Close(); ?> CacheExecute()方法的第一个参数是缓存文件(缓存文件被命名为adodb_*.cache)将被保留的时间,以秒计时;第二个参数是SQL声明。第一个参数是可选择的,若没有限定时间,默认值是3600秒,也就是1个小时。 值得一提的是,使用CacheExcute()方法时,需要将php.ini中的参数magic_quotes_runtime设为0。 也可以根据需要,在程序运行时动态修改它的值: set_magic_quotes_runtime(0); 注意:将上述代码放到调用数据库的指令之前,我们还可以在任何时候,通过调用CacheFlush()来清除过时的缓存。 10.生成下拉列表菜单 ADODB特意为Web开发任务提供几个通用的方法。其中,最有用的是GetMenu()方法,通过抽取数据库的记录集,自动地生成表单及菜单列表框。 下面介绍的就是从数据库动态构建下拉菜单(Option)的例子。 Connect("localhost", "root", "root", "library") or die("Unable to connect!"); // 构造并执行一个查询 $query = "SELECT title, id FROM library"; $result = $db->Execute($query) or die("Error in query: $query. " . $db->ErrorMsg()); //显示HTML下拉列表菜单 echo $result->GetMenu("library", '', false); // 关闭数据库连接 $db->Close(); ?> GetMenu()方法需要传入参数,用来控制列表框的行为。上例中第一个参数是列表框的名字(这个例子为“library”);第二个参数是显示时默认的值,可以为空,从第一个记录开始;第三个参数,指定列表框的项目是否为空;第四个参数,控制是否允许用户多选。 上例的显示结果如下: 可以看到,该列表菜单内容是从library表抽取的记录,列表框的名字为“library”,在记录集中,ID是菜单选项的值,名称为菜单框显示的元素。 由此可以看出,GetMenu()方法可以大幅度简化Web开发任务,大大减少代码量。 11.输出到文件 ADODB还允许我们将记录输出为一个不同形式的文件:如逗号分隔符CSV文件,制表符表格,甚至于HTML形式的表格。 这些功能属于ADODB的附属功能,在使用时需要包含相关ADODB类文件,下面是样例的内容。 Connect("localhost", "root", "passwd", "library") or die("Unable to connect!"); // 构造并执行一个查询 $query = "SELECT title, id FROM library"; $result = $db->Execute($query) or die("Error in query: $query. " . $db->ErrorMsg()); // 返回一个CSV字符串 echo rs2csv($result); // 关闭数据库的连接 $db->Close(); ?> 输出结果如下: title,id Mystic River,15 Where Eagles Dare,16 XML and PHP,17 我们也可以去除结果中第一行,即字段的名称,使用脚本格式如下: // 返回一个 CSV 字符串 echo rs2csv($result, false); 脚本的输出结果将没有字段名称,如下: Mystic River,15 Where Eagles Dare,16 XML and PHP,17 ADODB还提供生成制表符或分隔符文件功能,使用rs2tab()方法: Connect("localhost", "root", "root", "library") or die("Unable to connect!"); // 构造并执行一个查询 $query = "SELECT title, id FROM library"; $result = $db->Execute($query) or die("Error in query: $query. " . $db->ErrorMsg()); // 返回一个TAB制表符分隔的字符串 echo rs2tab($result); // 关闭数据库连接 $db->Close(); ?> 显示结果如下: title id Mystic River 15 Where Eagles Dare 16 XML and PHP 17 ADODB还提供生成HTML表格的功能,使用rs2html()方法: Connect("localhost", "root", "passwd", "library") or die("Unable to connect!"); // 构造并执行一个查询 $query = "SELECT title, id FROM library"; $result = $db->Execute($query) or die("Error in query: $query. " . $db->ErrorMsg()); // 返回一个HTML格式的表格 echo rs2html($result); // 关闭数据库连接 $db->Close(); ?> 输出结果如下: 17.5 PHPLib PHPLib可能是伴随PHP一同成长最老的数据库抽象层(但和ADODB相比,它只算是一个MySQL抽象类库),当前最新版本为7.4a。我们只需要它的数据库访问功能,除了支持MySQL外,它也同时支持访问Oracle 8以上版本的数据库。 PHPLib访问SQL数据的类名为DB_Sql,包含在db_mysql.inc.php文件中。我们要使用的仅是它的数据库抽象类,直接用require()和include()包含进来就可以进行开发了。下面是开发实例,并且所用类库都是最新版本,这些都可以在光盘中找到。 17.5.1 使用PHPLib查询首先定义一个配置文件config.inc.php,用于扩展PHPLib的MySQL类:DB_Sql。代码内容如下: query($sql); ?> 上面代码引用的db.inc.php用于扩展DB_Sql类,主要是重新声明连接的数据库和账号,以及出错信息的处理和显示功能,如下所示。 Host = DB_HOST; $this->Database = DB_NAME; $this->User = DB_USER; $this->Password = DB_PASSWORD; } function halt($msg) { printf("[/td][/table]Database Error: %s
\n", $msg); printf("MySQL Error: %s (%s)
\n", $this->Errno, $this->Error); printf("Please contact administrator and report the "); printf("exact error message.
\n"); die("Session halted."); } } ?> 我们还以symbols表为例,查询该表的数据,然后全部显示出来。 query($sql); ?> next_record()) { $country = $db->f("country"); $animal = $db->f("animal"); $cname = $db->f("cname"); echo ""; echo("国家: $country 代表动物: $animal ($cname) \n"); echo ""; } ?> 显示结果如下: 关于$db->next_record(),这是一个没有返回值的语句,它的功能是将当前表的指针向前移动并更新Record、Error、Row、Errno的方法。 while($db->next_record()) { … } 我们根据它的指针循环操作,直至到达尾记录,若条件不满足,则while退出循环。 $name = $db->f("name"); 给$name变量分配的是数据字段name的值。 echo("国家: $country 代表动物: $animal ($cname)
\n"); echo("
\n\n"); 打印出一行数据,也可不用变量赋值,而是直接引用方法返回的字段名即可,但是这种方法没有上面程序的可读性好。 while($db->next_record()) { echo("国家: ".$db->f("country")." 代表动物: " .$db->f("animal")."(".$db-f("cname")."
\n"); echo("
\n\n"); } 17.5.2 创建GetAll方法我们也可以开发一个函数,像ADODB或PDO那样,让抽出的记录生成一个二维关联数组。这样,PHPLib同样也可以与Smarty引擎协同工作,并且工作更方便,请看如下代码。 query($sql); $Array = array(); $j= 0; while($db->next_record()) { $Array[$j]= $db->Record; $j++; } return $Array; } ?> 然后把该方法放在一个文件中,比如func.inc.php,同样保存在lib目录下。这样,我们再不用每次都重复使用while循环,直接调用该函数即可。 在浏览器上的显示结果如下: Array ( [0] => Array ( [0] => 2 [id] => 2 [1] => China [country] => China [2] => dragon [animal] => dragon [3] => 龙 [cname] => 龙 ) ) GetAll方法产生的$getAnimal关联数组和ADODB的是一样的,这样,我们可以让PHPLib和Smarty匹配,把该数组抛给模板显示即可。 虽然没有ADODB、PDO那么强大,但PHPLib小巧方便,对于系统负载相对较小。如果仍在旧版本下或虚拟主机环境下进行开发,使用它也是不错的选择。 17.6 小结在本章中,我们一起了解了PDO及ADODB数据库类与抽象层在Web应用程序中的应用开发。其中包括记录集的概念(resultsets),如何连续获得表信息,如何使用PDO、ADODB进行优化查询,提高查询效率,以及结果缓存化的方法,如何输出HTML或其他格式文本的方法。 通过前面的介绍,相信您已经了解了数据库抽象层的特点和重要性:良好且丰富的功能和方法,方便使用,良好伸缩性与可移植性。 下一章,我们将要讨论PHP中热门且较实用的内容——页面模板化与Smarty技术。 |