【问题标题】:MySQL get products with multiple filter valuesMySQL获取具有多个过滤器值的产品
【发布时间】:2017-04-16 01:35:35
【问题描述】:

问题:
我如何获得所有Red 和尺寸S 的产品? 那意味着我只能得到产品2: Product (color:green,red => size:S)

说明:
我在从应用了某些过滤器的数据库中选择产品时遇到了问题。您将在下面找到我的表,并查看SQLfiddle 了解真正的 SQL。

product

id | title
------------------------------
 1 | Product (color:green => size:S)
 2 | Product (color:green,red => size:S)
 3 | Product (color:red)

filter

id | name
------------
 1 | Color
 2 | Size

filter_value

id | filter_id | name
---------------------
 1 |         1 | green
 2 |         1 | red
 3 |         2 | S

product_filter_value

id | product_id | filter_id | filter_value_id
---------------------------------------------
 1 |          1 |         1 |               1
 2 |          1 |         2 |               3
 3 |          2 |         1 |               1
 4 |          2 |         1 |               2
 5 |          2 |         2 |               3
 6 |          3 |         1 |               2

要选择过滤值为red 的所有产品,我运行以下查询:

# ALL COLOR RED
SELECT p.*
FROM product p 
  LEFT JOIN product_filter_value pfv ON p.id = pfv.product_id
WHERE (pfv.filter_id IN ('1')) 
  AND (pfv.filter_value_id IN ('2'))
GROUP BY p.id
LIMIT 10 OFFSET 0;

要选择过滤值为S 的所有产品,我运行以下查询:

# ALL SIZE S
SELECT p.*
FROM product p 
  LEFT JOIN product_filter_value pfv ON p.id = pfv.product_id
WHERE (pfv.filter_id IN ('2')) 
  AND (pfv.filter_value_id IN ('3'))
GROUP BY p.id
LIMIT 10 OFFSET 0;

但是我如何获得所有Red 和尺寸S 的产品? 那意味着我只能得到产品2: Product (color:green,red => size:S)

【问题讨论】:

    标签: mysql


    【解决方案1】:

    让我们从寻找一件事的简单基础开始。 它可以是大小、颜色或您的真实数据所具有的任何其他内容。 查询可能是

    SELECT
          pfv.product_id,
          f.name
       from
          product_filter_value pfv
             join filter_value fv
                ON pfv.filter_value_id = fv.id
                  AND fv.name = "what you are looking for"
             JOIN filter f
                ON pfv.filter_id = f.id
    

    现在,您可以为同一个查询中加入的每个唯一身份执行多个操作。 我将为每个被过滤的独特事物添加别名表的后缀。 如果适用,您甚至可以通过附加子查询连接(例如 ProdBrand 别名)添加品牌

    SELECT
          p.product_id,
          p.title,
          ProdColor.name as Color,
          ProdSize.name as Size
       from
          Product p
             JOIN ( select 
                          pfv.Product_id,
                          fv.name
                       from 
                          product_filter_value pfv
                             JOIN filter_value fv
                                ON pfv.filter_value_id = fv.id
                               AND fv.name = "red"
                             JOIN filter f
                                ON pfv.filter_id = f.id
                               AND f.name = "Color" ) ProdColor
                ON P.id = ProdColor.Product_ID
             JOIN ( select
                          pfv.Product_id,
                          fv.name
                       from
                          product_filter_value pfv
                            JOIN filter_value fv
                               ON pfv.filter_value_id = fv.id
                                 AND fv.name = "S"
                            JOIN filter f
                               ON pfv.filter_id = f.id
                               AND f.name = "Size" ) ProdSize
                ON P.id = ProdSize.Product_ID
    

    现在,如果您想尝试多种颜色或尺寸, 只需将每个子查询的条件更改为...

     AND INLIST( fv.name, "red", "blue" )  -- example for red OR blue
    
     AND INLIST( fv.name, "S", "M" )  -- example for Small OR Medium
    

    这可能是一个很长的查询,但单独查看每个部分非常简单。其目的是颜色、尺寸、品牌等的一个标准。您只是将其作为获得产品的整体条件的一部分加入。

    我可以在 this working sample 找到我使用原始架构的 SQLFiddle

    【讨论】:

    • 我没有测试你的查询。 CGritton 的答案似乎要简单得多。我不确定性能。另一方面,您的查询使用过滤器名称而不是 id。
    【解决方案2】:

    我无法让 sql fiddle 工作,但我想我还是会试一试。对于不同的过滤器类型,您可能必须有不同的连接。表别名 pfv1 的连接用于 filter1,pfv2 用于过滤器 2。

    SELECT *
    FROM product p 
      LEFT JOIN product_filter_value pfv1 ON p.id = pfv1.product_id and pfv1.filter_id = 1
      LEFT JOIN product_filter_value pfv2 ON p.id = pfv2.product_id and pfv2.filter_id = 2
    WHERE (pfv1.filter_id IN ('1')) 
      AND (pfv1.filter_value_id IN ('2'))
      and (pfv2.filter_id IN ('2')) 
      AND (pfv2.filter_value_id IN ('3'))
    GROUP BY p.id
    LIMIT 10 OFFSET 0;
    

    看看没有where子句的整个集合可能会很有趣:

    SELECT *
    FROM product p 
      LEFT JOIN product_filter_value pfv1 ON p.id = pfv1.product_id and pfv1.filter_id = 1
      LEFT JOIN product_filter_value pfv2 ON p.id = pfv2.product_id and pfv2.filter_id = 2
    
    + ------- + ----------------------------------- + ------- + --------------- + -------------- + -------------------- + ------- + --------------- + -------------- + -------------------- +
    | id      | title                               | id      | product_id      | filter_id      | filter_value_id      | id      | product_id      | filter_id      | filter_value_id      |
    | 1       | Product (color:green => size:S)     | 1       | 1               | 1              | 1                    | 2       | 1               | 2              | 3                    |
    | 2       | Product (color:green,red => size:S) | 3       | 2               | 1              | 1                    | 5       | 2               | 2              | 3                    |
    | 2       | Product (color:green,red => size:S) | 4       | 2               | 1              | 2                    | 5       | 2               | 2              | 3                    |
    | 3       | Product (color:red)                 | 6       | 3               | 1              | 2                    |         |                 |                |                      |
    + ------- + ----------------------------------- + ------- + --------------- + -------------- + -------------------- + ------- + --------------- + -------------- + -------------------- +
    4 rows
    

    产品 2 有两条记录,因为它有两种颜色,而产品 3 没有最后四列的值,因为它没有尺寸属性。

    【讨论】:

    • SQL Fiddle 确实坏了,似乎没有这么好的服务。您的第一个示例似乎运行良好。我不确定你的第二个。我仍然需要 filter_value_id 参与。
    • 第二个例子只是为了深入了解没有过滤器会发生什么。很高兴第一个对你有用。
    【解决方案3】:

    您可以单独获取 2 个元素,将它们连接起来,然后过滤它们(以易于阅读的 word 格式)

    给定(顺便说一下,你的 sql fiddle 不会加载)

    /*
    drop table if exists product;
    create table product(id int ,title varchar(50));
    insert into product values
    ( 1 , 'Product (color:green => size:S)' ),
    ( 2 , 'Product (color:green,red => size:S)' ),
    ( 3 , 'Product (color:red)' );
    
    drop table if exists filter;
    create Table filter(id int, name varchar(5));
    insert into filter values
    ( 1 , 'Color'),
    ( 2 , 'Size');
    
    drop table if exists filter_value;
    create table filter_value( id int, filter_id int, name varchar(5));
    insert into filter_value values
    ( 1 ,         1 , 'green'),
    ( 2 ,         1 , 'red'),
    ( 3 ,         2 , 'S');
    
    drop table if exists product_filter_value;
    create Table product_filter_value(id int, product_id int, filter_id int, filter_value_id int);
    insert into product_filter_value values
    ( 1 ,          1 ,         1 ,               1),
    ( 2 ,          1 ,         2 ,               3),
    ( 3 ,          2 ,         1 ,               1),
    ( 4 ,          2 ,         1 ,               2),
    ( 5 ,          2 ,         2 ,               3),
    ( 6 ,          3 ,         1 ,               2);
    */
    

    子查询 s 获取颜色,子查询 t 获取大小

    select u.stitle product
    from
    (
    select s.* , t.* from 
    (
    select p.id sid,p.title stitle,fv.name sfvname,f.name sname
    from    product p
    join  product_filter_value pfv on pfv.product_id = p.id
    join    filter_value  fv on fv.id = pfv.filter_value_id
    join  filter f on f.id = fv.filter_id
    where f.name = 'color'  
    ) s
    join
    (
    select p.id tid,p.title ttitle,fv.name tfvname,f.name tfname
    from    product p
    join  product_filter_value pfv on pfv.product_id = p.id
    join    filter_value  fv on fv.id = pfv.filter_value_id
    join  filter f on f.id = fv.filter_id
    where f.name = 'size'
    ) t on s.stitle = t.ttitle 
    ) u
    where u.sname = 'color' and u.sfvname = 'red' and u.tfname = 'Size' and u.tfvname = 'S'
    

    【讨论】:

    • SQLfiddle 确实有效,但现在它对我来说也坏了。我总是遇到 SQLfiddle 的问题。
    猜你喜欢
    • 2013-10-20
    • 2014-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-24
    • 1970-01-01
    • 1970-01-01
    • 2022-01-13
    相关资源
    最近更新 更多