选择多对多关系的另一面

时间:2013-12-01 15:27:23

标签: mysql join

背景

我有三个表:( SQL小提琴:http://sqlfiddle.com/#!2/f7b33/11

产品

+----+-----------+
| id |  product  |
+----+-----------+
|  1 | product_1 |
|  2 | product_2 |
|  3 | product_3 |
+----+-----------+

products_features

+------------+------------+
| product_id | feature_id |
+------------+------------+
|          1 |          1 |
|          1 |          2 |
|          1 |          3 |
|          1 |          4 |
|          2 |          1 |
|          2 |          3 |
|          3 |          4 |
+------------+------------+

功能

+----+-----------+
| id |  feature  |
+----+-----------+
|  1 | feature_1 |
|  2 | feature_2 |
|  3 | feature_3 |
|  4 | feature_4 |
+----+-----------+

然后我选择这样:

SELECT products.product,
       GROUP_CONCAT(features.feature) AS features
FROM   products
       LEFT JOIN products_features
              ON product_id = products.id
       LEFT JOIN features
              ON products_features.feature_id = features.id
GROUP  BY products.id

得到类似的东西:

+-----------+-----------------------------------------+
|  product  |                features                 |
+-----------+-----------------------------------------+
| product_1 | feature_1,feature_2,feature_3,feature_4 |
| product_2 | feature_1,feature_3                     |
| product_3 | feature_4                               |
+-----------+-----------------------------------------+

问题

所以,一切都很棒,但是,我想做的只是拥有feature_1和feature_3的东西,同时还能获得其他功能。

换句话说,我想写一个能让我得到的查询:

+-----------+-----------------------------------------+
|  product  |                features                 |
+-----------+-----------------------------------------+
| product_1 | feature_1,feature_2,feature_3,feature_4 |
| product_2 | feature_1,feature_3                     |
+-----------+-----------------------------------------+

我试过了:

SELECT products.product,
       GROUP_CONCAT(features.feature) AS features
FROM   products
       LEFT JOIN products_features
              ON product_id = products.id
       RIGHT JOIN features
              ON products_features.feature_id = features.id AND features.feature in ('feature_1','feature_3')

GROUP  BY products.id

但我当然得到:

+-----------+---------------------+
|  product  |      features       |
+-----------+---------------------+
| (null)    | feature_4,feature_2 |
| product_1 | feature_1,feature_3 |
| product_2 | feature_3,feature_1 |
+-----------+---------------------+

因此,虽然我现在知道product_1和product_2是具有这些功能的那些,但我看不到他们拥有的其他功能。

哪些查询可以让我指定feature_1和feature_3并获得以下回复?

+-----------+-----------------------------------------+
|  product  |                features                 |
+-----------+-----------------------------------------+
| product_1 | feature_1,feature_2,feature_3,feature_4 |
| product_2 | feature_1,feature_3                     |
+-----------+-----------------------------------------+

2 个答案:

答案 0 :(得分:3)

我认为最常见的方法是使用having子句:

SELECT products.product,
       GROUP_CONCAT(features.feature) AS features
FROM   products
       LEFT JOIN products_features
              ON product_id = products.id
       LEFT JOIN features
              ON products_features.feature_id = features.id
GROUP  BY products.id
HAVING sum(features.feature = 'feature_1') > 0 and
       sum(features.feature = 'feature_3') > 0;

having语句中的每个子句都计算给定要素出现的次数。 and要求两个要素都在最终结果集中。

编辑:

鉴于你的陈述结构,你也可以这样做:

HAVING find_in_set('feature_1', features) > 0 and
       find_in_set('feature_3', features) > 0;

这是有效的,因为您正在生成包含要素列表的列,并且您使用逗号作为该列表的分隔符。

答案 1 :(得分:1)

group_concat返回数据然后将结果拆分回前端是不行的。它不仅会导致资源的低效使用,而且还可能导致分裂错误(例如:想象当某个功能出现时会发生什么,并且恰好包含,)。

不使用group_concat的第三个主要原因是它对长度有限制。我没有重新解释方向盘,但请查看question以获取更多信息。

最好的方法是返回给定产品的所有匹配功能,然后只需循环处理它们。它应该很简单(实际上,大多数UI组件,无论是否为Web,都希望收到一个集合来显示它们,这就是你发送给它们的内容)。

此外,您很可能已经拥有要检查的功能的ID,因此检查它们而不是字符串会更有效。

我最后的评论是你实际上并不需要左连接。左连接将返回左侧的所有元素,无论它们是否在右侧匹配。但是,您需要右侧有2个元素(2个特征),这使得查询相互矛盾。你只需要一个内部联接。

这是我要使用的查询:

SELECT p.product, f.feature FROM products p
JOIN (
  SELECT product_id FROM products_features
  WHERE feature_id IN (1, 3)
  GROUP BY product_id
  HAVING count(*) = 2
) pf ON p.id = pf.product_id
JOIN products_features pf2 ON p.id = pf2.product_id
JOIN features f ON pf2.feature_id = f.id

这是该查询的fiddle