需要MySQL JOIN才能返回产品所属的每个类别,即使只搜索一个类别

时间:2015-06-21 01:08:59

标签: mysql join sql-view sqlperformance

我定义了以下表格:

CREATE TABLE products (
    product_id INTEGER(11) NOT NULL AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL,
    section VARCHAR(255) DEFAULT NULL,
    PRIMARY KEY (product_id)
) ENGINE=MyISAM;

CREATE TABLE categories (
    category_id INTEGER(11) NOT NULL AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL,
    PRIMARY KEY (category_id)
) ENGINE=MyISAM;

CREATE TABLE product_categories (
    product_id INTEGER(11) NOT NULL,
    category_id INTEGER(11) NOT NULL,
    PRIMARY KEY (product_id, category_id)
) ENGINE=MyISAM;

实际上还有更多,这是优化更大,更复杂的查询的一部分。部分原因是将一些缓慢的子查询移动到视图中,到目前为止已经有很多帮助。

当我添加categories / product_categories表并且允许用户按products.section或categories.category_id搜索时,查询变得非常慢。 UI将这些作为搜索参数传递给我,并且我试图获得每个产品的一行,其中包含其ID,名称,部分以及与之关联的以逗号分隔的类别名称列表。通过以下视图和查询,我能够更快地完成这项任务:

CREATE OR REPLACE
ALGORITHM = MERGE
VIEW view_products_with_categories
AS
SELECT 
    p.product_id,
    p.name,
    p.section,
    pc.name AS category
products p
LEFT JOIN product_categories pc on p.product_id = pc.product_id
LEFT JOIN categories c ON pc.category_id = c.category_id;

SELECT
    product_id,
    name,
    section,
    GROUP_CONCAT(DISTINCT category ORDER BY category) AS categories
FROM view_products_with_categories
GROUP BY product_id;

假设我们有以下几行:

product_id  name               section          category_id category
332913      Model Train Engine child-and-baby   1160        child-and-baby>baby-and-pre-schooltoys>playsets
332913      Model Train Engine child-and-baby   1308        toys>baby-and-preschool>playsets
332913      Model Train Engine child-and-baby   1312        toys>carstrains-and-planes>cars-and-vehicles

上面的简单查询给出了以下内容:

product_id  name                section         categories
332913      Model Train Engine  child-and-baby  child-and-baby>baby-and-pre-schooltoys>playsets,toys>baby-and-preschool>playsets,toys>carstrains-and-planes>cars-and-vehicles

没关系,正如预期的那样。但是,我希望用户能够通过category_id进行搜索。目前,我们的UI在类别名称上做了一些自动完成的魔术,并为动态生成的SQL添加了一个过滤器,其中包含了category_id。如果我在GROUP_CONCAT查询中离开了category_id,那么它将是1160.假设他们想要搜索第二个(1308),所以我们像这样修改查询:

SELECT
    product_id,
    name,
    section,
    GROUP_CONCAT(DISTINCT category ORDER BY category) AS categories
FROM view_products_with_categories
WHERE category_id = 1308
GROUP BY product_id;

现在我们得到以下内容:

product_id  name                section         categories
332913      Model Train Engine  child-and-baby  toys>baby-and-preschool>playsets

再一次,正是你所期待的。但是,客户希望查看与产品相关的所有类别,这些类别包含他们正在搜索的一个或多个类别。那么,让我们制作一些简化的样本数据来向您展示我正在寻找的东西:

product_id  name       section     category_id  category
1           product_1  section_1   1            category_1
1           product_1  section_1   2            category_2 
1           product_1  section_1   3            category_3
2           product_2  section_2   3            category_3
2           product_2  section_2   4            category_4 
2           product_2  section_2   5            category_5

如果用户搜索category_id = 3,我希望他们获得以下内容:

product_id  name      section   categories
1           product_1 section_1 category_1, category_2, category_3 
2           product_2 section_2 category_3, category_4, category_5 

但我目前只得到:

product_id  name      section   categories
1           product_1 section_1 category_3 
2           product_2 section_2 category_3

在没有子查询的情况下,我无法找到一种方法,其缓慢是我首先转向视图的原因。我希望有一些令人眼花缭乱的明显我失踪,可能是因为睡眠不足,所以任何帮助都会受到赞赏。

更新:我还应该提一下,产品可能不属于任何类别,因此我的代码中的LEFT JOIN。

1 个答案:

答案 0 :(得分:1)

以下查询有效:(虽然我没有使用该视图)

select pc1.product_id, products.name, products.section,  
  group_concat(categories.name)
from products
inner join product_categories pc1 
on (pc1.product_id =    products.product_id and pc1.category_id =  3)
inner join product_categories pc2 
on (pc2.product_id = products.product_id)
inner join categories  
on (categories.category_id = pc2.category_id)
group by pc1.product_id, products.name, products.section

如果您想使用该视图,以下内容将起作用:

SELECT  vpc.product_id,vpc.name,vpc.section,
GROUP_CONCAT(DISTINCT category ORDER BY category) AS categories
FROM view_products_with_categories vpc
inner join product_categories pc 
on (pc.product_id = vpc.product_id and pc.category_id=3) 
GROUP BY vpc.product_id, vpc.name, vpc.section;