使用多对多关系使查询量保持不变

l3zydbqr  于 2021-06-23  发布在  Mysql
关注(0)|答案(2)|浏览(360)

我有一个实体,它有两个多对多关系:
Product ,它可以有许多类别和许多标记。一 Tag 或者 Category 也可以有很多产品。
目前的架构是:

products
-------------
product_id
name

categories
-------------
category_id
name

tags
-------------
tag_id
name

products_categories
-------------
product_id
category_id

products_tags
-------------
product_id
tag_id

我需要在一个单一的页面上显示一个产品及其所有标签和类别的列表。
问题是我执行的查询数 2n + 1 其中n是页面中列出的产品数。
基本上首先我要做的是:

select product_id, name from products

然后在我的应用程序中的一个循环中执行这些查询(在伪代码中):

tags = []
categories = []
for each product
    tags[product_id] = select t.tag_id, t.name 
                       from products_tags as pt 
                       join tags t 
                           on pt.tag_id = t.tag_id
                       where pt.product_id = {product_id}

    categories[product_id] = select c.category_id, c.name 
                             from products_categories as pc 
                             join categories c 
                                 on pc.category = c.category_id
                             where pc.product_id = {product_id}
end for each

有没有一个好的方法使执行的查询数独立于查询的记录数?
编辑
对于每个产品,我需要以以下格式显示数据:

-------------------------------------------------------
| Product name: A good smartphone                     |
| Categories: Tech, Smartphone                        |
| Tags: smartphone, some-smartphone-brand, 4g         |
|                                                     |
-------------------------------------------------------
3hvapo4f

3hvapo4f1#

您可以使用单个查询从数据库中获取所有信息,并在应用程序级别根据您的需要构造数据,下面的查询将多次返回每个产品,但具有不同的标签/类别。

select p.product_id, p.name, t.tag_id, t.name tag_name, c.category_id, c.cat_name 
from product p
join products_tags as pt on pt.product_id = p.product_id
join tags t on pt.tag_id = t.tag_id
join products_categories as pc on pc.product_id = p.product_id
join categories c on pc.category = c.category_id
order by p.product_id

伪码

products = []
tags = []
categories = []
var productObj= {};
var product_id =false;
for each product
    /**
    * If product_id != product.id from loop iteration it means this is different product from previous iteration, Push this product object in `products[]`
    */
    if(product.id != product_id){
        products[product.id] = product;
        product_id = product.id;
    }
    /**you can put additional check here before insert in array if value is not present  `products[product.id][tags]` */
    products[product.id][tags][] = tag_name;

    /**you can put additional check here before insert in array if value is not present  `products[product.id][categories]` */ 
    products[product.id][categories][]= cat_name;
end for each

产品的示例对象/数据结构

[ 
    1 => [
    name => A good smartphone,
    categories => [Tech, Smartphone],
    tags => [smartphone, some-smartphone-brand, 4g]
    ],  
    2 => [
    name: Another product,
    categories => [Cat1, Cat2],
    tags => [Tag1, Tag2, .....]
    ],
.....
]
oaxa6hgo

oaxa6hgo2#

您可以尝试这样做(它依次使用带有标记和类别的联接,并通过列表类型来区分它们):

select *, 'CAT' as table_type
from products p left join products_categories pc left join categories c  
ON p.product_id = pc.product_id and pc.category_id = c.category_id
UNION ALL
select *, 'TAG' as table_type
from products p left join products_tags pt left join tags t  
ON p.product_id = pt.product_id and pt.tag_id = t.tag_id
order by p.product_id

相关问题