返回所有相关 WHERE 匹配的行

Returning row where all related WHEREs match

我需要 return 匹配多对多关系上所有 WHERE 语句的结果。解释一下:

我有一个这样的table结构。

CREATE TABLE `product` (
  `product_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`product_id`));

CREATE TABLE `product_has_tag` (
  `product_id` INT UNSIGNED NOT NULL,
  `tag_id` INT UNSIGNED NOT NULL
);

CREATE TABLE `tag` (
  `tag_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `value` VARCHAR(30) NULL,
  PRIMARY KEY (`tag_id`));

现在我遇到这样的情况:

Product: {
  [product_id:1], 
  [product_id:2], 
  [product_id:3]};
Tags: {
  [tag_id:1, value:'XX'],
  [tag_id:2, value:'YY']}
ProductHasTag: {
  [product_id:1, tag_id:1],
  [product_id:1, tag_id:2],
  [product_id:2, tag_id:1],
  [product_id:3, tag_id:2]}

基本上,只有一种产品有两个标签,其余只有一个。现在我需要一个 SQL 搜索条件,它将 return 仅包含两个标签的产品。像

SELECT * FROM `product` 
LEFT JOIN product_has_tag USING(product_id) 
LEFT JOIN tag USING(tag_id) 
WHERE value LIKE '%X%' AND value LIKE '%Y%'

这显然不起作用,因为搜索值不在一个标签中,而是在两个标签中。 任何帮助表示赞赏。

您需要像处理两个不同的列一样处理这两个不同的标签。这需要一些花哨的连接工作。

此查询为您提供符合第一个条件的产品。

SELECT p.*, t1.value tag1
  FROM product p
  JOIN product_has_tag pt1 ON p.product_id =  pt1.product_id
  JOIN tag t1 ON pt1.tag_id = t1.tag_id AND t1.value LIKE '%X%'

您可以向此查询添加一些内容以获得您想要的两个标签。

SELECT p.*, t1.value tag1, t2.value tag2
  FROM product p
  JOIN product_has_tag pt1 ON p.product_id =  pt1.product_id
  JOIN tag t1 ON pt1.tag_id = t1.tag_id AND t1.value LIKE '%X%'
  JOIN product_has_tag pt2 ON p.product_id =  pt2.product_id
  JOIN tag t2 ON pt2.tag_id = t2.tag_id AND t2.value LIKE '%Y%'

JOIN 项目将导致此查询将您的产品行与两个匹配的标签隔离开来。

您在此查询中的 value LIKE '%match%' 模式将导致性能不佳。如果您可以使用 value LIKE 'match%'value = 'match' 性能将能够利用 value 列上的索引。

您可以汇总匹配的标签,并且只有 return 有 2 个匹配的结果。

SELECT * FROM product
LEFT JOIN product_has_tag USING(product_id) 
LEFT JOIN tag USING(tag_id) 
WHERE value LIKE '%X%' OR value LIKE '%Y%' 
GROUP BY product_id  HAVING COUNT(DISTINCT tag_id) = 2

您只需要将 WHERE 更改为 HAVING 并使用 SUM CASE WHEN

SELECT `product`.`product_id`    -- *
FROM `product` 
LEFT JOIN product_has_tag 
  USING(product_id) 
LEFT JOIN tag 
   USING(tag_id) 
GROUP BY `product`.`product_id`
HAVING SUM(CASE WHEN `value` LIKE '%X%' THEN 1 END) > 0
   AND SUM(CASE WHEN `value` LIKE '%Y%' THEN 1 END) > 0

SqlFiddleDemo