2

我试图找出一个查询来检索特定产品所在的类别列表。

我有一个名为 products 的数据库,其中包含三个名为 categories、product_to_sec_cat_assoc 和 products 的表。

+--------------------+ +--------------------+ +--------------------------+
| Products           | | categories         | | product_to_sec_cat_assoc |
+--------------------+ +--------------------+ +--------------------------+
| product_id         | | category_id        | | product_id               |
| pri_category_id    | | category_name      | | category_id              |
| name               | | url_link           | +--------------------------+
+--------------------+ +--------------------+ 

由于并非所有产品都有次要类别,因此我直接在产品条目中将其主要类别分配给产品。如果它们也在其他次要类别中,我使用名为 product_to_sec_cat_assoc 的关联表来关联更多类别。

我在编写查询时遇到问题,我想在其中创建一个显示特定类别中所有产品的页面。每个产品列表还显示了它们所在的各种次要类别。

我目前的查询是:

select t.*, 
GROUP_CONCAT(c.category_name order by pri_category_id, c.category_id) as category_names, 
GROUP_CONCAT(c.url_link) as category_links from products t
left join product_to_sec_category_assoc a using (id)
left join categories c on t.pri_category_id = c.category_id or c.category_id = a.category_id
where t.pri_category_id = 12 
or (a.category_id = 12 and a.id = t.id)
group by t.title

但是,由于某种原因,如果我将产品添加到多个次要类别中,当我查看其中一个次要类别中的产品时,它只会显示我正在查看的主要类别和次要类别。另一方面,如果我查看主要类别列表,它会显示主要类别及其所有次要类别。

例如,假设我在电子产品主要类别以及平板电脑和移动设备次要类别中拥有 iPad。如果我访问电子产品类别列表,它会在电子产品、平板电脑、手机中显示 iPad。如果我进入移动类别,它会在电子、移动中显示。如果我进入平板电脑类别,它会显示在电子产品、平板电脑类别中。

我遇到的另一个问题是查询也没有正确排序 GROUP_CONCAT(category_name) 和 GROUP_CONCAT(category_link) 以便 category_names 及其链接处于匹配顺序。

4

1 回答 1

2

考虑到您的设计(产品与类别有两种不同的关系),您的问题相当棘手。问题在于,在您的WHERE子句中,您实际上是在过滤掉其余的产品类别,只返回用户所在的类别和/或其主要类别。

相反,您必须首先在辅助类别表或其主要类别字段中选择具有特定类别的产品(在子选择中完成FROM),然后结果连接到辅助类别表 - 这样您就可以获得所有属于特定类别的产品的类别(无论是否是主要类别):

SELECT
    a.*,
    GROUP_CONCAT(DISTINCT CONCAT(c.category_name, '||', c.url_link) ORDER BY c.category_name) AS categories
FROM
    (
        SELECT 
            aa.product_id,
            aa.pri_category_id,
            aa.name
        FROM
            products aa
        LEFT JOIN
            product_to_sec_category_assoc bb ON 
                aa.product_id = bb.product_id
                AND bb.category_id = 12
        WHERE
            aa.pri_category_id = 12
            OR bb.product_id IS NOT NULL
    ) a
LEFT JOIN
    product_to_sec_cat_assoc b ON a.product_id = b.product_id
LEFT JOIN
    categories c ON 
        a.pri_category_id = c.category_id
        OR b.category_id = c.category_id
GROUP BY
    a.product_id
ORDER BY
    a.title

两个12代表用户当前所在的类别。

您必须注意的一件事是,对于具有至少一个次要类别的每个产品,OR第二个总是连接两行 - 一行将始终是该产品的主要类别。LEFT JOIN因此,在连接中,对于为产品列出的每个次要类别,您将在其上添加产品的主要类别,从而导致重复。DISTINCT这就是为什么你必须在GROUP_CONCAT()函数中引入。

在 中GROUP_CONCAT(),我决定将category_nameurl_link字段合并在一起,用 . 分隔||。根据您在应用程序中使用的语言,您可以根据该分隔符拆分两者。这比尝试匹配两个不同列中的顺序要好。


也许更好的设计是在产品和类别之间建立多对多关系,并在交叉引用表中有一个字段指示该类别是否是主要的。架构看起来像:

+---------------+ +-------------------+ +----------------+
| products      | | product_cat_assoc | | categories     |
+---------------+ +-------------------+ +----------------+
| product_id    | | product_id        | | category_id    |
| name          | | category_id       | | category_name  |
+---------------+ | is_primary        | | url_link       |
                  +-------------------+ +----------------+

现在您只需要关心基于一种关系而不是两种关系来检索类别。该is_primary字段可以是 TINYINT(1),包含01指示它是否是主要的。在此设计中,所有产品应始终在表中至少有一个对应的行product_cat_assoc表示主要类别。其他行将是次要类别。

然后要检索特定类别中产品的所有类别列表,查询将非常简单:

SELECT
    a.*,
    GROUP_CONCAT(CONCAT(c.category_name, '||', c.url_link) ORDER BY b.is_primary DESC) AS categories
FROM
    (
        SELECT 
            aa.product_id, 
            aa.name
        FROM 
            products aa
        INNER JOIN 
            product_cat_assoc bb ON aa.product_id = bb.product_id AND bb.category_id = 12
    ) a
INNER JOIN
    product_cat_assoc b ON a.product_id = b.product_id
INNER JOIN
    categories c ON b.category_id = c.category_id
GROUP BY
    a.product_id
ORDER BY
    a.name
于 2012-06-18T18:33:23.917 回答