【问题标题】:How to select a distinct column value matching multiple criterea如何选择匹配多个条件的不同列值
【发布时间】:2020-03-09 07:13:30
【问题描述】:

我有一个包含具有以下结构的属性的表:

id: bigint unsigned autoincrement
product_id: bigint foreign key 
attribute_id: bigint foreign key
value:  varchar(100) 

我可以通过以下方式查询一个条件:

SELECT DISTINCT product_id FROM product_attributes WHERE attribute_id = ? AND value = ?

但是,我需要能够找到符合多个此类条件的产品,并且出于性能原因希望避免多个数据库查询。简单地使用 AND 添加更多条件是行不通的,因为它们将涉及相同的列,例如:

SELECT DISTINCT product_id FROM product_attributes WHERE attribute_id = 1 AND value = 'Blue'
INTERSECT
SELECT DISTINCT product_id FROM product_attributes WHERE attribute_id = 2 AND value = '36'
INTERSECT
SELECT DISTINCT product_id FROM product_attributes WHERE attribute_id = 3 AND value = 'slim'

我已经阅读了 INTERSECT 语句,它似乎可以工作,但我读到 MySQL 不支持它,通过 MySQL 8 文档搜索没有产生相关结果,并且我认为上面的查询是正确的产生了MySQL 错误。

我还了解到,使用内部连接也可以实现类似的功能,但我发现的所有示例都涉及多个表。可能还有一种更好或更简单的方法来编写我没有想到的查询。或者,实际上最好只发送多个查询并在 MySQL 之外计算交集(尽管我会感到非常惊讶)我非常感谢过去做过类似事情的任何人的帮助。

【问题讨论】:

  • 你听说过OR运营商吗?或者你只是在使用AND
  • @Zeljka 要是这么简单就好了。使用 OR 运算符会产生误报。我会得到符合任何标准的产品。因此,例如,我会得到不是蓝色但纤细或尺寸为 36 的产品。或者我可能会得到蓝色但不纤细且尺寸不是 36 的产品。
  • 好的,您没有使用OR 运算符,试试thiswhere (attribute_id = 1 AND value = 'Blue') OR (attribute_id = 2 AND value = '36') OR (attribute_id = 3 AND value = 'slim') 并且不要使用任何相交,只需一个符合此条件的查询
  • EAV 模式设计充满了麻烦。慰问。
  • 这是一个常见问题解答。在考虑发布之前,请阅读手册和谷歌任何错误消息或您的问题/问题/目标的许多清晰、简洁和精确的措辞,有和没有您的特定字符串/名称和站点:stackoverflow.com 和标签;阅读许多答案。如果您发布问题,请使用一个短语作为标题。请参阅How to Ask 和投票箭头鼠标悬停文本。 PS您的帖子不包含您正在谈论的查询类别的清晰准确的陈述。在你这样做之前,你无法有效地搜索。您还希望我们从片段和示例中猜测。

标签: mysql inner-join key-value entity-attribute-value intersect


【解决方案1】:

你需要使用聚合来统计条件集合的匹配行数,并断言它等于条件数:

SELECT product_id
FROM product_attributes
WHERE (attribute_id, value) IN ((1, 'Blue'), (2, '36'), (3, 'slim'))
GROUP BY product_id
HAVING COUNT(*) = 3

【讨论】:

  • 我不得不说这在我的初始测试中绝对有效并且看起来很棒。不过,在我选择接受的答案之前,我会做一些基准测试。
  • @Kaan 绝对 - 在你弄清楚什么是最好的之前,你不应该接受任何东西。请注意,如果条件可以重复(例如,attribute_id = 1value = 'Blue' 超过一行),则需要在 HAVING 子句中使用 COUNT(DISTINCT attribute_id, value)
  • 您的意思是多行具有相同的product_id、attribute_id 和value?因为我不应该这样。那还有什么意义呢?但是,我可以拥有具有相同 product_id 和 attribute_id 但值列不同的行。
  • @Kaan 是的,相同的 product_id、attribute_id 和 value。正如你所说,这有什么意义,但相信我,我已经看到了......
  • 我相信你 :) 感谢您的提醒
【解决方案2】:

这是键/值存储问题。

做你想做的事,脖子会有点痛。使用 JOIN 操作将值转换为一行。像这样。

    SELECT p.product_id, 
           color.value AS color,
           size.value AS size,
           cut.value AS cut
      FROM ( SELECT DISTINCT product_id FROM product_attributes ) p
      LEFT JOIN product_attributes color ON color.product_id = p.product_id 
                                        AND color.attribute_id = 1
      LEFT JOIN product_attributes size  ON size.product_id = p.product_id 
                                        AND size.attribute_id = 2
      LEFT JOIN product_attributes cut   ON cut.product_id = p.product_id 
                                        AND cut.attribute_id = 3

这会生成一个结果集,每个产品/颜色/尺寸/剪裁组合有一行

然后你可以像这样过滤那个结果集

SELECT * 
  FROM (
      SELECT p.product_id, 
             color.value AS color,
             size.value AS size,
             cut.value AS cut
        FROM ( SELECT DISTINCT product_id FROM product_attributes ) p
        LEFT JOIN product_attributes color ON color.product_id = p.product_id 
                                          AND color.attribute_id = 1
        LEFT JOIN product_attributes size  ON size.product_id = p.product_id 
                                          AND size.attribute_id = 2
        LEFT JOIN product_attributes cut   ON cut.product_id = p.product_id 
                                          AND cut.attribute_id = 3
       ) combinations
 WHERE color='Blue' AND size='36' AND cut='slim'

MySQL 的查询计划器足够聪明,只要有适当的索引,它就不会像您想象的那样运行缓慢。

FROM 子句从您的 product_attributes 表中生成产品 ID 的完整列表,以连接到特定属性。如果您有其他产品表,请使用该表而不是 SELECT DISTINCT...

【讨论】:

  • 谢谢,尽管您在两个查询中都错过了AS colorAS size 之后的逗号。否则这绝对有效。我确实认为@Nick 的答案更优雅,但我很想知道哪个性能更高,哪个负载更大,所以我会在选择一个正确之前运行一些基准测试
  • 感谢您的逗号。我经常从像我这样的查询中创建视图,以供不太了解 SQL 复杂性的人使用。 @nick 的查询可能比我的要快一点。
猜你喜欢
  • 2020-01-04
  • 2021-02-11
  • 1970-01-01
  • 1970-01-01
  • 2021-10-25
  • 2011-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多