es.phpget_user 函数连接到包含用户表的数据库并检索给定用户的 ID。get_files 函数连接到文件表并检索与给定用户相关联的文件行。
做所有这些事情的一个更好办法是将数据加载到一个数据库中,然后执行查询,比如下面的查询。
清单 9. Getfiles_good.php该代码不仅更短,而且也更容易理解和高效。我们不是执行两个查询, 而是执行一个查询。
尽管该问题听起来有些牵强,但是在实践中我们通常总结出所有的表应该在同一个数据库中,除非有 非常迫不得已的理由。
问题 4:不使用关系
关系数据库不同于编程语言,它们不具有数组类型。相反,它们使用表之间的关系来创建对象之间的 一到多结构,这与数组具有相同的效果。我在应用程序中看到的一个
问题是,工程师试图将数据库当作编 程语言来使用,即通过使用具有逗号分隔的标识符的文本字符串来创建数组。请看下面的模式。
清单 10. Bad.
sql[/code]
系统中的一个用户可以具有多个文件。在编程语言中,应该使用数组来
表示与一个用户相关联的文件。在本例中,
程序员选择创建一个 files 字段,其中包含一个由逗号分隔的 文件 id 列表。要得到一个特定用户的所有文件的列表,程序员必须首先从用户表中读取行,然后解析文件 的文本,并为每个文件运行一个单独的 SELECT 语句。该代码如下所示。
清单 11. Get.php该技术很慢,难以维护,且没有很好地利用数据库。惟一的解决
方案是重新架构模 式,以将其转换回到传统的关系形式,如下所示。
清单 12. Good.sql这里,每个文件都通过 user_id 函数与文件表中的用户相关。这可能与任何将多 个文件看成数组的人的思想相反。当然,数组不引用其包含的对象 —— 事实上,反之亦然。但是在关系 数据库中,
工作原理就是这样的,并且查询也因此要快速且简单得多。清单 13 展示了相应的 PHP 代码。
清单 13. Get_good.php这里,我们对数据库进行一次
查询,以获得所有的行。代码不复杂,并且它 将数据库作为其原有的用途使用.
问题 5:n+1 模式
我真不知有多少次看到过这样的大型应用程序, 其中的代码首先检索一些实体(比如说客户), 然后来回 地一个一个地检索它们,以得到每个实体的详细信息。我们将其称为 n+1 模式,因为查询要执行这么多 次 —— 一次查询检索所有实体的列表,然后对于 n 个实体中的每一个执行一次查询。当 n=10 时这还 不成其为问题,但是当 n=100 或 n=1000 时呢?然后肯定会出现低效率问题。清单 14 展示了这种模 式的一个例子。
清单 14. Schema.sql该模式是可靠的,其中没有任何错误。问题在于访问数据库以找到一个给
定作 者的所有书籍的代码中,如下所示。
清单 15. Get.php如果您看看下面的代码,您可能会想,“嘿,这才是真正的清楚明了。” 首先,得 到作者 id,然后得到书籍
列表,然后得到有关每本书的信息。的确,它很清楚明了,但是其高效吗?回答 是否定的。看看只是检索 Jack Herrington 的书籍时要执行多少次查询。一次获得 id,另一次获得书籍 列表,然后每本书执行一次查询。三本书要执行五次查询!
解决方案是用一个函数来执行大量的查询,如下所示。
清单 16. Get_good.php[/code]
现在检索列表需要一个快速、单个的查询。这意味着我将很可
能必须具有几个这些类型的具有不同参数的方法,但是实在是没有选择。如果您想要具有一个扩展的 PHP 应用程序,那么必须有效地使用数据库,这意味着更智能的查询。
本例的问题是它有点太清晰了。通常来说,这些类型的 n+1 或 n*n 问题要微妙得多。并且它们只 有在数据库管理员在
系统具有性能问题时在系统上运行查询剖析器时才会出现。
结束语
数据库是强大的工具,就跟所有强大的工具一样,如果您不知道如何正确地使用就会滥用它们。识别 和解决这些问题的诀窍是更好地理解底层技术。长期以来,我老听到业务逻辑编写人员抱怨,他们不想要 必须理解数据库或 SQL 代码。他们把数据库当成对象使用,并疑惑性能为什么如此之差。
他们没有认识到,理解 SQL 对于将数据库从一个困难的必需品转换成强大的联盟是多么重要。如果 您每天使用数据库,但是不熟悉 SQL,那么请阅读 The Art of SQL,这本书写得很好,实践性也很强, 可以指导您基本了解数据库。