【发布时间】:2010-11-04 00:03:16
【问题描述】:
我目前正在使用 PHP 和 MySQL 设计一个基于 Kohana 框架的应用程序。我正在使用内置的 ORM,事实证明它非常有用。一切正常,但我非常关心某些页面上运行的查询数量。
设置
例如,有一个页面,您可以在该页面上查看一个充满部分的类别,而这些部分又是产品。这以表格形式列出。每个产品(可能)都有许多属性、标志、层级定价中断。这些都必须在表格中表示出来。
多少查询?
就查询而言:类别必须查询其中的所有部分,并且这些部分必须查询它们包含的所有产品。还不错,但是每个产品都必须查询它的所有产品属性、层级定价和标志。因此,向一个类别添加更多产品会多次增加查询(因为我目前主要使用 ORM)。一个部分中有几百个产品将导致几百个查询。小查询,但这仍然不好。
到目前为止...
所有的键都被索引。我可以通过一个查询提取所有信息(参见下面的编辑),但是,正如您想象的那样,这将导致大量冗余数据分布在每个产品的多行中,每个额外的(例如)属性,标志等
我不反对放弃 ORM 以用于应用程序的显示部分并使用查询构建甚至原始 SQL。
解决方案实际上可能非常简单,而我现在对此一无所知,老实说,这将是一种解脱。或者也许不是。我不知道。如果我的任何解释不足以理解问题,请询问,我会尝试举一个更好的例子。 (编辑:给出了更好的例子,见下文
虽然,附注... 不过可能有一些相关性:虽然我总是希望以最有效的方式设计应用程序,但这并不是一个每天会被点击数十或数百次的网站。它更像是一个管理应用程序,可能不会同时被几个人使用。我无法预见太多的重新加载,因为页面上的大部分数据编辑都是通过 AJAX 完成的。那么,如果每次加载此特定页面时在此页面上运行几百个查询(随着当前查看的部分中有多少产品而波动),我是否应该如此关心?只是一个侧面的想法,即使可以解决上述主要问题,我也更愿意这样做。
非常感谢!
编辑
根据几个答案,我似乎没有充分解释自己。因此,让我发布一个示例,以便您了解发生了什么。
不过,在示例之前,我还应该澄清两点:(1) 还有几个多对多关系,(2) 您可能会将我正在寻找的内容比作交叉表查询。
让我们简化并假设我们有 3 个主表: 产品(product_id、product_name、product_date_added) product_attributes (product_attribute_id, product_id, value) 通知(notification_id、notification_label)
还有 1 个枢轴表: product_notifications (notification_id, product_id)
我们将在一个表格中列出所有产品。在 ORM 中调用所有产品很简单。 因此,对于每个“产品”,我们都会列出 product_name 和 product_date_added。但是,我们还需要列出所有产品属性。每个产品有 0 个或多个。我们还必须显示产品有哪些通知,其中也有 0 个或更多。 所以目前,它的工作原理基本上是:
foreach ($products->find_all() as $product) //given that $products is an ORM object
{
echo $product->product_id; //lets just pretend these are surrounded by html
echo $product->product_name;
foreach ($products->product_attributes->find_all() as $attribute)
{
echo $attribute->value;
}
foreach ($products->notifications->find_all() as $notification)
{
echo $notification->notification_label;
}
}
这当然过于简单化了,但这就是我所说的原则。这工作已经很棒了。 然而,如您所见,对于每个产品,它必须查询其所有属性以获取适当的集合或行。
find_all() 函数将返回以下内容的查询结果:
SELECT product_attributes.* FROM product_attributes WHERE product_id = '#',通知也是如此。它会针对每个产品进行这些查询。
因此,对于数据库中的每个产品,查询的数量是该数量的几倍。
因此,虽然这很有效,但它的扩展性并不好,因为它可能会导致数百个查询。
如果我执行一个查询以获取一个查询中的所有数据,如下所示:
SELECT p.*, pa.*, n.*
FROM products p
LEFT JOIN product_attributes pa ON pa.product_id = p.product_id
LEFT JOIN product_notifications pn ON pn.product_id = p.product_id
LEFT JOIN notifications n ON n.notification_id = pn.notification_id
(再次过于简单化)。这会获取数据本身,但是对于产品具有的每个属性和通知,将返回一个包含冗余信息的额外行。
例如,如果我在数据库中有两个产品;一个有1个属性和1个标志,另一个有3个属性和2个标志,它将返回:
product_id, product_name, product_date_added, product_attribute_id, value, notification_id, notification_label
1, My Product, 10/10/10, 1, Color: Red, 1, Add This Product
2, Busy Product, 10/11/10, 2, Color: Blue, 1, Add This Product
2, Busy Product, 10/11/10, 2, Color: Blue, 2, Update This Product
2, Busy Product, 10/11/10, 3, Style: New, 1, Add This Product
2, Busy Product, 10/11/10, 3, Style: New, 2, Update This Product
不用说,这是很多多余的信息。每个产品返回的行数将是它具有的属性数乘以它具有的通知数。
ORM(或者,通常只是在循环中创建新查询)将每一行中的所有信息合并到它自己的对象中,从而可以更合理地处理数据。就是那块石头。在一个查询中调用信息可以消除可能数百个查询的需要,但会在行中创建大量冗余数据,因此不会返回简洁集合中的(一对多)关系数据。那是困难的地方。
抱歉拖了这么久,求详细点,哈哈,谢谢!
【问题讨论】:
-
一个好的 ORM 应该让您在初始查询中指定哪些内容应该作为 JOIN 加载,而不是稍后通过单独的查询延迟加载它们......
-
仅供参考,您可以运行
Database::instance()->last_query并向我们展示一些ORM 生成的查询吗?哦,对一个经过深思熟虑的问题 +1(现在越来越少有 1 个代表用户)。 -
我已经编辑了问题描述以包含正在生成的查询的简短示例。谢谢!就 JOINS 而言,我还进行了编辑,以显示当您尝试加入此数据时会发生什么。希望上面发布的示例数据能更清楚地说明问题是什么以及我正在努力克服的问题。非常感谢!