简体   繁体   English

使用许多一对多关系(ORM)减少MySQL中的查询

[英]Decreasing queries in MySQL with many one-to-many relationships (ORM)

I'm currently designing an application using PHP and MySQL, built on the Kohana framework. 我目前正在使用基于Kohana框架构建的PHP和MySQL设计应用程序。 I'm making use of the built in ORM and it has proved to be extremely useful. 我正在使用内置的ORM,事实证明它非常有用。 Everything works fine, but I'm very concerned with the number of queries being run on certain pages. 一切正常,但我非常关心在某些页面上运行的查询数量。

Setting 设置
For example, there's a page on which you can view a category full of sections, which are in turn full of products. 例如,有一个页面,您可以在其中查看充满各个部分的类别,这些部分又包含产品。 This is listed out in tabular format. 这以表格格式列出。 Each product has (possibly) many attributes, flags, tier pricing breaks. 每个产品都有(可能)许多属性,标志,等级定价中断。 This must all be represented in the table. 这必须全部在表格中表示。

How many queries? 有多少疑问?
As far as queries are concerned: The category must query all the sections within it, and those sections must query all the products they contain. 就查询而言:类别必须查询其中的所有部分,并且这些部分必须查询它们包含的所有产品。 Not too bad, but each product must then query all it's product attributes, tier pricing, and flags. 还不错,但每个产品必须查询它的所有产品属性,层定价和标志。 So, adding more products to a category increases the queries many times over (since I'm currently using the ORM primarily). 因此,向类别添加更多产品会使查询多次增加(因为我目前主要使用ORM)。 Having a few hundred products in a section will result in a couple hundred queries. 在一个部分中有几百个产品将导致几百个查询。 Small queries, but that is still not good. 小问题,但仍然不好。

So far... 至今...
All the keys are indexed. 所有键都被编入索引。 I can pull all of the information with a single query (see edit below), however, as you could imagine, this will result in a lot of redundant data spread out across multiple rows per each product, per each extra (eg) attribute, flag, etc. 我可以使用单个查询提取所有信息(请参阅下面的编辑),但是,正如您所能想象的那样,每个额外(例如)属性,这将导致每个产品在多行中分布大量冗余数据,国旗等

I'm not opposed to ditching the ORM for the displaying part of the application and going with query building or even raw SQL. 我并不反对放弃ORM来显示应用程序的部分并继续使用查询构建甚至原始SQL。

The solution for this could be actually be quite simple and I'm just ignorant of it right now, which would be a relief honestly. 对此的解决方案实际上可能非常简单,我现在只是对它一无所知,老实说这是一种解脱。 Or maybe it's not. 或许它不是。 I'm not sure. 我不确定。 If any of my explanation was not adequate enough to understand the problem just ask and I'll try to give a better example. 如果我的任何解释都不足以理解问题,那就问一下,我会试着给出一个更好的例子。 (Edit: Better example given, see below (编辑:给出更好的例子,见下文

Although, a side note... One thing that may have some relevance though: while I always want to have the application designed most efficiently, this isn't a site that's going to be hit dozens or hundreds of times a day. 虽然,旁注...但有一点可能有一些相关性:虽然我总是希望最有效地设计应用程序,但这不是一个每天会被打几十次或几百次的网站。 It's more of an administrative application, which probably won't be in use by more than a few individuals at once. 它更像是一个管理应用程序,可能不会被多个人同时使用。 I can't foresee too much reloading, as most of the editing of data on the page is done through AJAX. 我无法预见太多的重新加载,因为页面上的大部分数据编辑都是通过AJAX完成的。 So, should I care as much if on this page it's running a couple hundred queries (fluctuating with how many products are in the currently viewed section) are running each time this particular page is loaded? 那么,如果在这个页面上运行了几百个查询(当前查看的部分中有多少产品不一致),每次加载此特定页面时,我是否应该关注多少? Just a side thought, even so if it is possible to solve the main aforementioned problem I would prefer that. 只是一个想法,即便如此,如果有可能解决上述主要问题,我宁愿这样做。

Thank you very much! 非常感谢你!

EDIT 编辑
Based on a couple answers, it seems I didn't explain myself adequately. 基于几个答案,似乎我没有充分解释自己。 So, let me post an example so you see what's going on. 所以,让我发一个例子让你看看发生了什么。 Before the example though, I should also make two clarifications: (1) there are also a couple many-to-many relationships, (2) and you could possibly liken what I'm looking for to that of a crosstab query. 在这个例子之前,我还应该做两个澄清:(1)还有一对多对多的关系,(2)你可能把我正在寻找的东西比作交叉表查询。

Let's simplify and say we have 3 main tables: products (product_id, product_name, product_date_added) product_attributes (product_attribute_id, product_id, value) notifications (notification_id, notification_label) 让我们简化并说我们有3个主表:产品(product_id,product_name,product_date_added)product_attributes(product_attribute_id,product_id,value)通知(notification_id,notification_label)

And 1 pivot talbe: product_notifications (notification_id, product_id) 和1个支点talbe:product_notifications(notification_id,product_id)

We're going to list all the products in a table. 我们将列出表格中的所有产品。 It's simple enough in the ORM to call all the products. 在ORM中调用所有产品非常简单。 So per each 'products' we list the product_name and product_date_added. 因此,根据每个“产品”,我们会列出product_name和product_date_added。 However, we also need to list all the products attributes out. 但是,我们还需要列出所有产品属性。 There are a 0 or more of these per product. 每种产品有0或更多。 We also have to show what notifications a product has, of which there are 0 or more as well. 我们还必须显示产品的通知,其中还有0或更多。 So at the moment, how it works is basically: 所以目前,它的工作原理基本上是:

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

This is oversimplified of course, but this is the principle I'm talking about. 这当然过于简单,但这是我所说的原则。 This works great already. 这已经伟大工程 However , as you can see, for each product it must query all of it's attributes to get the appropriate collection or rows. 但是 ,正如您所看到的,对于每个产品,它必须查询其所有属性以获取适当的集合或行。 The find_all() function will return the query results of something along the lines of: SELECT product_attributes.* FROM product_attributes WHERE product_id = '#' , and similarly for the notifications. find_all()函数将返回以下行的查询结果: SELECT product_attributes.* FROM product_attributes WHERE product_id = '#' ,类似于通知。 And it makes these queries for each product. 它为每个产品进行了这些查询。
So, for every product in the database, the number of queries is a few times that amount. 因此,对于数据库中的每个产品,查询数量是该数量的几倍。 So, although this works well, it does not scale well, as it may potentially result in hundreds of queries. 因此,尽管效果很好,但它不能很好地扩展,因为它可能会导致数百个查询。

If I perform a query to grab all the data in one query, along the lines of: 如果我执行查询以获取一个查询中的所有数据,则执行以下操作:

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

(Again oversimplified). (再次过度简化)。 This gets the data per se, but per each attribute and notification a product has, an extra row with redundant information will be returned. 这将获取数据本身,但根据产品的每个属性和通知,将返回带有冗余信息的额外行。

For example, if I have two products in the database; 例如,如果我在数据库中有两个产品; one has 1 attribute and 1 flag and the other has 3 attributes and 2 flags, it will return: 一个有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

Needless to say that's a lot of redundant information. 不用说,这是一个很多冗余的信息。 The number of rows returned per product would be the number of attributes it has times the number of notifications it has. 每个产品返回的行数将是它拥有的通知数量的属性数。

The ORM (or, just creating the new queries in the loop in general) consolidates all of the information in each row into it's own object, allowing for the data to be handled more logically. ORM(或者,通常只是在循环中创建新查询)将每行中的所有信息合并到它自己的对象中,从而允许更逻辑地处理数据。 That's the rock. 那是摇滚乐。 Calling the information in one query eliminates the need for possibly hundreds of queries, but creates lots of redundant data in rows and therefore does not return the (one/many)-to-many relationship data in succinct sets. 在一个查询中调用信息消除了对可能数百个查询的需要,但是在行中创建了大量冗余数据,因此不会以简洁的集合返回(一个/多个)到多个关系数据。 That's the hard place. 那是个难点。

Sorry it's so long, trying to be thorough, haha, thanks! 对不起,这么久,试图彻底,哈哈,谢谢!

An interesting alternative is to handle your reads and your writes with completely separate models. 一个有趣的替代方案是使用完全独立的模型处理您的读取和写入。 (Command Query Separation). (命令查询分离)。 Sophisticated object models (and ORMS) are great for modeling complex business behavior, but are lousy as interfaces for querying and displaying information to users. 复杂的对象模型(和ORMS)非常适合于复杂的业务行为建模,但是作为查询和向用户显示信息的接口很糟糕。 You mentioned that you weren't opposed to ditching the ORM for rendering displays -- well, that's exactly what many software architects nowadays suggest. 你提到你并不反对放弃使用ORM渲染显示器 - 嗯,这正是许多软件架构师现在提出的建议。 Write a totally different interface (with its own optimized queries) for reading and reporting on data. 编写一个完全不同的界面(具有自己的优化查询),用于读取和报告数据。 The "read" model could query the same database that you use with your ORM backed "write" model, or it could be a separate one that is denormalized and optimized for the reports/screens you need to generate. “读取”模型可以查询与ORM支持的“写入”模型一起使用的相同数据库,也可以是针对您需要生成的报告/屏幕进行非规范化和优化的单独数据库。

Check out these two presentations. 看看这两个演示文稿。 It may sound like overkill (and it may be if your performance requirements are very low), but it's amazing how this technique makes so many problems just go away. 这可能听起来有点矫枉过正(如果你的性能要求非常低,可能就是这样),但是这种技术让这么多问题消失的感觉真是太棒了。

A good ORM should handle this for you. 一个好的ORM应该为你处理这个问题。 If you feel you must do it manually, you can do this. 如果您认为必须手动执行此操作,则可以执行此操作。

Fetch all the categories you need in a single query and store the primary key ID's in a PHP array. 在单个查询中获取所需的所有类别,并将主键ID存储在PHP数组中。

Run a query similar to this: 运行类似这样的查询:

mysql_query('SELECT yourListOfFieldsHere FROM Products WHERE Product_id IN ('.implode(',', $categoryIDs).')');

This should give you all the products that you need in a single query. 这应该为您提供单个查询中所需的所有产品。 Then use PHP to map these to the correct categories and display accordingly. 然后使用PHP将这些映射到正确的类别并相应地显示。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM