选择具有多个标签的行...有更好的方法吗?

时间:2012-01-11 18:23:09

标签: mysql join tags many-to-many

我正在尝试使用标记系统从数据库中选择产品。我已经读过,实现这一点的最好方法是通过多对多关系,因为当有大量记录时,使用LIKE'%tag%'会变慢。我还阅读了this问题,在多个标签上匹配,您必须为所请求的每个标签进行连接。

我有3个表:shop_products,shop_categories和shop_products_categories。例如,我需要能够找到同时具有“鲜花”和“浪漫”标签的产品。

SELECT p.sku, p.name, p.path FROM shop_products p
  LEFT JOIN shop_products_categories pc1 ON p.sku = pc1.product_sku
  LEFT JOIN shop_categories c1           ON pc1.category_id = c1.id
  LEFT JOIN shop_products_categories pc2 ON p.sku = pc2.product_sku
  LEFT JOIN shop_categories c2           ON pc2.category_id = c2.id
WHERE c1.path = 'flowers' AND c2.path = 'romance'

这是我正在构建的演示查询,用于在为相关PHP编码之前检查它是否有效,并且它可以工作。但这真的是最好的方法吗?我发现很难相信没有比为搜索的每个标签进行连接更好的方法。 谢谢你的建议。 :)

2 个答案:

答案 0 :(得分:3)

无需进行多次连接。如果需要匹配所有标记,可以使用IN子句和子查询,如下所示:

select p.sku, p.name, p.path 
from shop_products p
where p.sku in (
    select pc.product_sku 
    from shop_products_categories pc 
    inner join shop_categories c on pc.category_id = c.id
    where c.path in ('flowers', 'romance')
    group by pc.product_sku
    having count(distinct c.path) = 2
)

请注意,您需要将数字2调整为您要匹配的唯一标记的数量。请注意,如果这是用户输入的数据,他们会两次输入相同的标签。

答案 1 :(得分:1)

SELECT
    p.sku, p.name, p.path  
FROM
    shop_products p 
    INNER JOIN
    (
        SELECT A.sku FROM
        (
            SELECT product_sku sku FROM shop_products_categories
            WHERE category_id=(SELECT id FROM shop_categories WHERE path='flowers')
        ) A
        INNER JOIN
        (
            SELECT product_sku sku FROM shop_products_categories
            WHERE category_id=(SELECT id FROM shop_categories WHERE path='romance')
        ) B
        USING (sku)
    ) flowers_and romance
    USING (sku)
;

确保您拥有以下索引:

ALTER TABLE shop_categories ADD INDEX (path,id);
ALTER TABLE shop_categories ADD UNIQUE INDEX (path);
ALTER TABLE shop_products_categories ADD INDEX (product_sku,category_id);