【问题标题】:Selecting against subsets of a list in MySQL在 MySQL 中选择列表的子集
【发布时间】:2010-02-15 02:04:34
【问题描述】:

我是个初学者,我有两个表:“产品”和“产品属性”。

这是一些虚构的数据(实际的东西涉及更多的表格)

产品表:

product_id | product_name                  
10         |   aaa                           
11         |   bbb   
12         |   ccc

产品属性表:

attribute_id | product_id
      21     |    10         
      23     |    10         
      24     |    10         
      21     |    11         
      24     |    11         
      21     |    12         
      25     |    12         

每种产品都有多个可能的属性。我有一个属性 ID 列表,例如 (21,10,25),我需要选择其属性是该列表子集的所有产品。

是否可以在一个查询中执行此操作?

当我过滤 (21,24) 所需的输出时,只返回产品 11 (bbb)

当我过滤 (21,23,24) 时,期望的输出是返回产品 10 和 11。

当我过滤 (21) 所需的输出时,不返回任何结果(因为所有产品都至少有一个其他属性)。

【问题讨论】:

    标签: mysql select


    【解决方案1】:

    如果您假装您的过滤器在表格中:

    select * 
    from product p
    where not exists (
        select 1
        from attributes a
        where a.product_id = p.product_id
        and not exists(
            select 1
            from filter f
            where f.id_attribute = a.id_attribute))
    

    如果它在构造查询中:

    select * 
    from product p
    where not exists (
        select 1
        from attributes a
        where a.product_id = p.product_id
        and attribute_id not in (<list>))
    

    这不是我的想法,所以可能有错别字。

    【讨论】:

      【解决方案2】:

      假设您的产品表称为 Product 并且该表中的 ID 列仅称为 Id:

      SELECT * from Product p where p.Id IN 
        (Select id_product from ProductAttributes where id_attribute in (21, 23, 24))
      

      【讨论】:

      • 是的,但这也会选择具有列表中一个属性而另一个不在列表中的产品。我已经这样做了。我需要它们在列表中包含所有属性(就像我说的那样),所以属性表中的每一行都带有该 product_id
      【解决方案3】:

      这应该只返回每个 id 的所有属性都完全包含在列表中的那些 id:

      select attribute_match.id_product from
       (select id_product, count(*) c from attributes
        where id_attribute in (21, 10, 25)
        group by id_product) attribute_match,
       (select id_product, count(*) c_count from attributes
        group by id_product) attribute_total
      where attribute_match.id_product = attribute_total.id_product
            and attribute_match.c = attribute_total.c
      

      【讨论】:

      • 这在 (21,23,24) 的情况下不起作用(仅选择 1 个产品)
      • 我认为修改后的查询应该可以工作——第一个内部选择应该生成 10,3 11,2,第二个内部选择应该生成 10,3 11,2,所以外部选择应该在两者上都匹配10 和 11。
      • 是的,现在可以了,发布了一种更简单的方法,但您仍然可以投票
      • 我认为如果这些 id 碰巧没有属性,则使用“不在列表中”方法的答案将错误地返回产品 id。
      • 你是对的,但每个产品至少有两个属性是脚本的设计方式。我忘了说明
      【解决方案4】:
      select
          P.id,
          P.name,
          count(P.id) as matched_attr_count,
          count(PA.a_id) as total_attr_count
      from
          product_attributes PA
          left join product P on P.id = PA.p_id and PA.a_id in (21,23,24)
      group by
          PA.p_id
      having
          matched_attr_count = total_attr_count;
      

      【讨论】:

      • 这个解决方案似乎也有效,除了它为每个产品返回更多行,因此它会通过 P.id_product 使一组受益编辑:不,它不起作用.. 同样的问题,它不过滤一个产品的所有属性,其中一个在列表中就足够了
      • 是的,但还有一个问题是它没有过滤所有属性
      • 我先误会你了。现在看起来修复了吗?
      • 在我的第二篇文章中查看示例数据
      • 是的,这行得通,谢谢,发布了一个更简单的答案,但我检查了所有
      【解决方案5】:

      直到 MySQL 支持 EXCEPT 查询组合,

      SELECT product_id
        FROM attributes
        WHERE product_id NOT IN (
             SELECT product_id
               FROM attributes 
               WHERE attribute_id NOT IN (21, 23, 24)
           )
        GROUP BY product_id
      UNION
      SELECT id 
        FROM products AS p
        LEFT JOIN attributes AS a
          ON p.id = a.product_id
        WHERE a.product_id IS NULL
      

      如果您希望只包含具有所有给定属性的产品,请在第一个外部查询中添加 HAVING COUNT(*)=n 子句,其中“n”是属性列表的长度。

      【讨论】:

      • 相同,根据我的示例在 (21,23,24) 的情况下不起作用
      • @Bogdan:原始代码用于问题修订版 3。更新以匹配当前修订版 (7)。
      • 我没有修改问题,只是通过示例更好地解释了它。不管怎样,谢谢你的努力,你得到了我的一票
      • @Bogdan:这个问题是模棱两可的,但现在不是了。澄清就是修改。
      【解决方案6】:

      根据您的见解,我进一步优化了它,只使用了 1 个这样的 COUNT 语句:

      SELECT * ,COUNT(p.product_id) AS c FROM product_attribute pa 
      LEFT JOIN products p ON pa.product_id = p.product_id AND pa.attribute_id NOT IN ($filter_list)
      GROUP BY pa.product_id
      HAVING c=0
      

      有效吗? :)

      编辑:
      该代码不会返回产品名称或它可能具有的其他字段。这是正确的:

      SELECT * ,COUNT(pa.product_id ) AS c FROM products p
      LEFT JOIN product_attribute pa ON pa.product_id = p.product_id AND pa.attribute_id NOT IN ($filter)
      GROUP BY p.product_id
      HAVING c=0
      

      【讨论】:

        【解决方案7】:

        让我发布简单的虚构数据(实际内容涉及更多表格)

        产品

        product_id | product_name                  
        10          |   aaa                           
        11     |    bbb  
        

        product_attribute

        attribute_id | product_id  <br>
        21     |    10         
        23     |    10         
        24     |    10         
        21     |    11         
        24     |    11 
        

        我想要那个:

        • 当我过滤 (21,24) 以仅返回产品 11 (bbb)
        • 当我过滤 (21,23,24) 以返回两种产品时
        • 当我过滤 (21) 时只返回无(因为没有产品只有该属性)

        【讨论】:

        • 这东西在 prestashop 里,如果你需要一个模块来按属性过滤,但是更复杂的表和关系
        • 我建议您编辑问题并将其添加到问题中。
        • 此类信息应包含在您的原始问题中。如果您想稍后包含更多信息,可以编辑您的问题。我已经为你编辑了你的问题。
        猜你喜欢
        • 2016-01-12
        • 2012-08-10
        • 2022-12-17
        • 1970-01-01
        • 2015-11-16
        • 2017-03-30
        • 2015-12-17
        • 1970-01-01
        • 2013-09-10
        相关资源
        最近更新 更多