热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

五、处理行集

五、处理行集现实生活中的动态、数据驱动的web应用程序彼此非常不同,因为它们的复杂性取决于它

五、处理行集

现实生活中的动态、数据驱动的 web 应用程序彼此非常不同,因为它们的复杂性取决于它们所服务的目的。然而,它们几乎都有一些共同的特点。这些特性之一是能够对长结果列表进行分页,以便于使用和加快页面加载时间。

正确的分页需要计算从数据库返回的总行数、页面大小(这是一个可配置的选项)和当前页面的数量。基于此数据,很容易将起始偏移量计算到结果集中,以仅显示行的子集。

在本章中,我们将研究:


  • 如何检索 PDO 返回的结果集中的行数

  • 如何从指定的行号开始获取结果


检索结果集中的行数

正如我们在第 2 章中所讨论的, PDOStatement::rowCount()方法不会在查询中返回正确的行数。(MySQL 和 SQLite 都返回零。)这种行为的原因是数据库管理系统在返回查询的最后一行之前实际上不知道这个数字。 mysql_num_rows()函数(以及其他数据库的类似函数)返回行计数的原因是,当您发出查询时,它会将整个结果集预加载到内存中。

虽然看起来很方便,但不建议使用此行为。如果查询返回 20 行,那么脚本可以负担内存使用。但是如果查询返回数十万行呢?它们都将保存在内存中,以便在高流量站点上,服务器可能会耗尽资源。

唯一的逻辑度量(也是 PDO 可用的唯一选项)是指示数据库计算自身的行数。无论查询有多复杂,都可以重写,使用 SQL COUNT()函数只返回满足主查询的行数。

让我们来看一下在我们的应用程序中使用的查询。(我们将只检查返回多行的查询。)


  • In books.php we have a query that joins two tables to present the list of books along with their authors :

    ```php
    SELECT authors.id AS authorId, firstName, lastName, books.*
    FROM authors, books WHERE author=authors.id ORDER BY title;

    ```

    要获取此查询返回的行数,我们应该将其重写为如下所示:

    ```php
    SELECT COUNT(*) FROM authors, books WHERE author=authors.id;

    ```

    请注意,这里不需要 ORDER BY子句,因为顺序对行数实际上并不重要。


  • In authors.php we simply select all the authors ordered by their last name and then their first name:

    ```php
    SELECT * FROM authors ORDER BY lastName, firstName;

    ```

    这只是改写为以下内容:

    ```php
    SELECT COUNT(*) FROM authors;

    ```


  • Another query that returns multiple rows is in author.php—it retrieves all the books written by a particular author:

    ```php
    SELECT * FROM books WHERE author=$id ORDER BY title;

    ```

    这可转化为以下内容:

    ```php
    SELECT COUNT(*) FROM books WHERE author=$id;

    ```


如您所见,我们以类似的方式重写了所有这些查询,将列列表替换为 COUNT(*)并修剪 ORDER BY子句。考虑到这一点,我们可以创建一个函数,该函数将接受包含要执行的 SQL 的字符串,并返回查询将返回的行数。此函数必须执行以下简单转换:


  • 在传递的字符串中将 SELECTFROM之间的所有内容替换为 COUNT(*)

  • 删除 ORDER BY及其后的所有文本。

实现此转换的最佳方法是使用正则表达式。与前几章一样,我们将使用 PCRE 扩展。我们将把这个函数放到 common.inc.php中,因为我们将在不同的地方调用它:

/**
* This function will return the number of rows a query will return
* @param string $sql the SQL query
* @return int the number of rows the query specified will return
* @throws PDOException if the query cannot be executed
*/
function getRowCount($sql)
{
global $conn;
$sql = trim($sql);
$sql = preg_replace('~^SELECT\s.*\sFROM~s', 'SELECT COUNT(*) FROM',
$sql);
$sql = preg_replace('~ORDER\s+BY.*?$~sD', '', $sql);
$stmt = $conn->query($sql);
$r = $stmt->fetchColumn(0);
$stmt->closeCursor();
return $r;
}

让我们运行函数以查看它的功能:


  1. 它将 PDO 连接对象($conn导入本地功能范围。

  2. 它从 SQL 查询的开头和结尾修剪可能的空格。

  3. preg_replace()的两个调用完成了转换查询的主要任务。

请注意我们是如何使用模式修饰符的,s修饰符指示 PCRE 将换行符与点匹配,D修饰符强制$与整个字符串的结尾匹配(不只是在第一个换行符之前)。我们使用这些修饰符来确保函数能够正确处理多行查询。

现在我们将修改这三个脚本,以显示它们返回的每个表中的行数。让我们从 books.php:开始

/**
* This page lists all the books we have
* PDO Library Management example application
* @author Dennis Popel
*/
// Don't forget the include
include('common.inc.php');
// Display the header
showHeader('Books');
// Get the count of books and issue the query
$sql = "SELECT authors.id AS authorId, firstName, lastName, books.*
FROM authors, books WHERE author=authors.id ORDER BY title";
$totalBooks = getRowCount($sql);
$q = $conn->query($sql);
$q->setFetchMode(PDO::FETCH_ASSOC);
// now create the table
?>
Total books:











// Now iterate over every row and display it
while($r = $q->fetch())
{
?>









}
?>
CoverAuthor and TitleISBNPublisherYearSummaryEdit




n/a


("$r[firstName] $r[lastName]")?>



Edit

Add book...
// Display footer
showFooter();

如您所见,修改非常简单,我们使用 $sql变量保存查询并将其传递给 getRowCount()函数和 $conn->query()方法。我们还在表上方显示一条消息,告诉我们数据库中有多少本书。

现在如果您刷新 books.php页面,您将看到以下内容:

Retrieving the Number of Rows in a Result Set

authors.php的变化类似:

/**
* This page lists all the authors we have
* PDO Library Management example application
* @author Dennis Popel
*/
// Don't forget the include
include('common.inc.php');
// Display the header
showHeader('Authors');
// Get the number of authors and issue the query
$sql = "SELECT * FROM authors ORDER BY lastName, firstName";
$totalAuthors = getRowCount($sql);
$q = $conn->query($sql);
// now create the table
?>
Total authors:








// Now iterate over every row and display it
while($r = $q->fetch(PDO::FETCH_ASSOC))
{
?>






}
?>
First NameLast NameBioEdit

Edit

Add Author...
// Display footer
showFooter();

authors.php现在应显示以下内容:

Retrieving the Number of Rows in a Result Set

最后, author.php将如下所示:

/**
* This page shows an author's profile
* PDO Library Management example application
* @author Dennis Popel
*/
// Don't forget the include
include('common.inc.php');
// Get the author
$id = (int)$_REQUEST['id'];
$q = $conn->query("SELECT * FROM authors WHERE id=$id");
$author = $q->fetch(PDO::FETCH_ASSOC);
$q->closeCursor();
$q = null;
// Now see if the author is valid - if it's not,
// we have an invalid ID
if(!$author) {
showHeader('Error');
echo "Invalid Author ID supplied";
showFooter();
exit;
}
// Display the header - we have no error
showHeader("Author: $author[firstName] $author[lastName]");
// Now get the number and fetch all the books
$sql = "SELECT * FROM books WHERE author=$id ORDER BY title";
$totalBooks = getRowCount($sql);
$q = $conn->query($sql);
$q->setFetchMode(PDO::FETCH_ASSOC);
// now display everything
?>

Author



















First Name
Last Name
Bio
Total books

Edit author...

Books










// Now iterate over every book and display it
while($r = $q->fetch()) {
?>







}
?>
TitleISBNPublisherYearSummary

// Display footer
showFooter();

输出应该是这样的。(为了节省空间,我将页面向下滚动了一点):

Retrieving the Number of Rows in a Result Set

您应该在 common.inc.php中切换 MySQL 和 SQLite,以确保这两个数据库都工作。

这种方法可能适用于许多情况,但并不适用于所有查询。一个这样的例子是使用 GROUP BY子句的查询。如果您使用 getRowCount()函数重写此类查询,您将得到不正确的结果,因为将应用分组,并且查询将返回多行。(行数将等于要分组的列中的不同值数。)

限制返回的行数

现在,当我们知道如何计算结果集中的行数时,让我们看看如何仅获取前 N 行。这里我们有两个选择:


  • 我们可以在 SQL 查询本身中使用特定于数据库的功能。

  • 我们可以自己处理结果集,并在获取所需数量的行后停止。


使用特定于数据库的 SQL

如果您主要使用 MySQL,那么您将熟悉 LIMIT x,y子句。例如,如果我们想获取按姓氏排序的前五位作者,可以发出以下查询:

SELECT * FROM authors ORDER BY lastName LIMIT 0, 5;

以下查询也可以执行相同的操作:

SELECT * FROM authors ORDER BY lastName LIMIT 5 OFFSET 0;

第一个查询适用于 MySQL 和 SQLite,第二个查询也适用于 PostgreSQL。但是,Oracle 或 MS SQL Server 之类的数据库不使用这种语法,因此这些查询将失败。

仅处理前 N 行

如您所见,特定于数据库的 SQL 不允许我们以独立于数据库的方式解决执行分页的任务。但是,我们可以像对所有行一样发出查询,而不需要 LIMIT....OFFSET子句。获取每一行后,我们可以增加计数器变量,以便在处理所需数量的行时中断循环。以下代码段可以用于此目的:

$q = $conn->query("SELECT * FROM authors ORDER BY lastName,
firstName");
$q->setFetchMode(PDO::FETCH_ASSOC);
$count = 1; while(($r = $q->fetch()) && $count <= 5)
{
echo $r['lastName'], '
';
$count++;
} $q->closeCursor();
$q = null;

注意循环条件,它检查计数器变量是否小于或等于 5。(当然,您可以在那里输入任何数字),并且它会验证是否仍有行要获取,因为如果没有更多行,我们必须中断循环。(例如,如果表只有 3 行,我们想显示其中的 5 行,我们应该在最后一行之后中断,而不是在计数器达到 5 行之后。)请注意,使用特定于数据库的 SQL 可以解决这种情况。

另一件重要的事情是对 PDOStatement::closeCursor()的调用(如前一段代码中的最后一行)。有必要告诉数据库我们不需要更多的行。如果不这样做,在同一 PDO 对象上发出的后续查询将导致异常,因为数据库管理系统无法处理新查询,而它们仍在发送前一查询中的行。这就是为什么我们必须在 author.php中调用此方法。

目前(对于 PHP 版本 5.2.1),可能需要通过将语句对象赋值为 null 来取消设置语句对象(如 author.php第 17 行)。另一方面,2007 年 4 月 1 日左右发布的至少一个 CVS 快照根本不需要关闭光标。但是,在使用完光标后调用 PDOStatement::closeCursor()仍然是一种很好的做法。

以任意偏移量开始

现在我们知道了如何处理指定数量的行,我们可以使用相同的技术跳过特定数量的行。假设我们想显示从第 6 页到第 10 页的作者(就像我们在显示第 2 页时,页面大小允许每页有 5 位作者):

$q = $conn->query("SELECT * FROM authors ORDER BY lastName,
firstName");
$q->setFetchMode(PDO::FETCH_ASSOC);
$count = 1;
while(($r = $q->fetch()) && $count <= 5)
{
$count++;
}
$count = 1;
while(($r = $q->fetch()) && $count <= 5)
{
echo $r['lastName'], '
';
$count++;
}
$q->closeCursor();
$q = null;

这里,第一个循环用于跳过必要的起始行,第二个循环显示请求的行子集。

这种方法可能适用于小型表,但其性能并不好。您应该始终使用特定于数据库的 SQL 来返回结果行的子集。如果需要数据库独立性,则应检查底层数据库软件并发出特定于数据库的查询。这样做的原因是,数据库可以对查询执行某些优化,使用更少的内存,以便在服务器和客户端之间交换更少的数据。

不幸的是,PDO 并没有提供独立于数据库的方法来有效地获取结果行的子集,因为 PDO 是一种连接抽象,而不是数据库抽象工具。如果您需要编写可移植代码,您应该探索 MDB2 等工具。

这种方法似乎比使用 PDOStatement::fetchAll()方法更复杂。实际上,我们可以将前面的代码重写如下:

$stmt = $conn->query("SELECT * FROM authors ORDER BY lastName,
firstName");
$page = $stmt->fetchAll(PDO::FETCH_ASSOC);
$page = array_slice($page, 5, 5);
foreach($page as $r)
{
echo $r['lastName'], '
';
}

虽然这段代码要短得多,但它有一个主要缺点:它指示 PDO 返回表中的所有行,然后获取其中的一部分。在我们的方法中,不必要的行被丢弃,循环指示数据库在返回足够的行后立即停止发送行。但是,在这两种情况下,数据库都必须向我们发送当前页面前面的行。

总结

在本章中,我们已经了解了如何处理无缓冲查询以及如何获取结果集的行数。我们还研究了一个无法避免特定于数据库的 SQL 的应用程序,因为这需要一个可能不合适的变通方法。但是,本章对于开发使用数据库的复杂 web 应用程序的人员应该有所帮助。

在下一章中,我们将讨论 PDO 的高级功能,包括持久连接和其他特定于驱动程序的选项。我们还将讨论事务,并研究更多的 PDOPDOStatement类的方法。


推荐阅读
  • PHP连接MySQL的三种方法及预处理语句防止SQL注入的技术详解
    PHP连接MySQL的三种方法及预处理语句防止SQL注入的技术详解 ... [详细]
  • PHP自学必备:从零开始的准备工作与工具选择 ... [详细]
  • 在JavaWeb开发中,文件上传是一个常见的需求。无论是通过表单还是其他方式上传文件,都必须使用POST请求。前端部分通常采用HTML表单来实现文件选择和提交功能。后端则利用Apache Commons FileUpload库来处理上传的文件,该库提供了强大的文件解析和存储能力,能够高效地处理各种文件类型。此外,为了提高系统的安全性和稳定性,还需要对上传文件的大小、格式等进行严格的校验和限制。 ... [详细]
  • JavaScript XML操作实用工具类:XmlUtilsJS技巧与应用 ... [详细]
  • Spring Boot 中配置全局文件上传路径并实现文件上传功能
    本文介绍如何在 Spring Boot 项目中配置全局文件上传路径,并通过读取配置项实现文件上传功能。通过这种方式,可以更好地管理和维护文件路径。 ... [详细]
  • 网站访问全流程解析
    本文详细介绍了从用户在浏览器中输入一个域名(如www.yy.com)到页面完全展示的整个过程,包括DNS解析、TCP连接、请求响应等多个步骤。 ... [详细]
  • 本文介绍了如何利用HTTP隧道技术在受限网络环境中绕过IDS和防火墙等安全设备,实现RDP端口的暴力破解攻击。文章详细描述了部署过程、攻击实施及流量分析,旨在提升网络安全意识。 ... [详细]
  • php更新数据库字段的函数是,php更新数据库字段的函数是 ... [详细]
  • 如何在Linux服务器上配置MySQL和Tomcat的开机自动启动
    在Linux服务器上部署Web项目时,通常需要确保MySQL和Tomcat服务能够随系统启动而自动运行。本文将详细介绍如何在Linux环境中配置MySQL和Tomcat的开机自启动,以确保服务的稳定性和可靠性。通过合理的配置,可以有效避免因服务未启动而导致的项目故障。 ... [详细]
  • 本文全面解析了 Python 中字符串处理的常用操作与技巧。首先介绍了如何通过 `s.strip()`, `s.lstrip()` 和 `s.rstrip()` 方法去除字符串中的空格和特殊符号。接着,详细讲解了字符串复制的方法,包括使用 `sStr1 = sStr2` 进行简单的赋值复制。此外,还探讨了字符串连接、分割、替换等高级操作,并提供了丰富的示例代码,帮助读者深入理解和掌握这些实用技巧。 ... [详细]
  • 本文总结了JavaScript的核心知识点和实用技巧,涵盖了变量声明、DOM操作、事件处理等重要方面。例如,通过`event.srcElement`获取触发事件的元素,并使用`alert`显示其HTML结构;利用`innerText`和`innerHTML`属性分别设置和获取文本内容及HTML内容。此外,还介绍了如何在表单中动态生成和操作``元素,以便更好地处理用户输入。这些技巧对于提升前端开发效率和代码质量具有重要意义。 ... [详细]
  • 本文探讨了将PEBuilder转换为DIBooter.sh的方法,重点介绍了如何将DI工具集成到启动层,实现离线镜像引导安装。通过使用DD命令替代传统的grub-install工具,实现了GRUB的离线安装。此外,还详细解析了bootice工具的工作原理及其在该过程中的应用,确保系统在无网络环境下也能顺利引导和安装。 ... [详细]
  • 配置php连接mysql,PHP连接
    本文目录一览:1、dw中php怎么连接mysql数据库 ... [详细]
  • C语言中全部可用的数学函数有哪些?2.longlabs(longn);求长整型数的绝对值。3.doublefabs(doublex);求实数的绝对值。4.doublefloor(d ... [详细]
  • mysql 数据目录更换_更改Mysql数据目录,这个坑你可能也趟过~
    原标题:更改Mysql数据目录,这个坑你可能也趟过~背景:上周网盘服务器由于要导入一个测试数据库的表。默认的mysql路径硬盘空间太小&# ... [详细]
author-avatar
大果粒和小果粒
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有