按选项筛选产品

时间:2017-10-27 10:48:26

标签: mysql sql database

我有以下数据库结构来存储产品选项。

DB

现在我有问题来筛选出只匹配给定选项的产品。首先我做了WHERE option_id IN(选项数组),但这会给我产品匹配任何选项,而不是解决方案。例如,用户只想过滤掉具有给定材料,颜色和尺寸的产品。如果我做WHERE option_id = 4 AND option_id = 6,例如我什么都没得到。

这是我的问题:

SELECT DISTINCT p.id AS id,
                ...
FROM products p
LEFT JOIN product_categories pc ON p.id = pc.product_id
LEFT JOIN product_images pi ON p.id = pi.product_id
LEFT JOIN product_options po ON p.id = po.product_id
WHERE p.product_active = 1
  AND po.option_id = 1 // only to get the idea
GROUP BY id
ORDER BY id DESC
LIMIT 0,
      12

只是提到它是PHP应用程序,用户从select元素中选择带有或不带多个属性的选项。

如何实现这个目标?

4 个答案:

答案 0 :(得分:4)

您可以使用having

SELECT p.id AS id,  ...
FROM products p JOIN
     product_categories pc
     ON p.id = pc.product_id LEFT JOIN
     product_images pi
     ON p.id = pi.product_id JOIN
     product_options po
     ON p.id = po.product_id
WHERE p.product_active = 1 AND
      po.option_id IN (4, 6)
GROUP BY p.id
HAVING COUNT(DISTINCT po.option_id) = 2
ORDER BY p.id DESC
LIMIT 0, 12;

HAVING子句指定给定的id有两个匹配选项。由于WHERE子句,这些是您唯一关心的两个选项。

我没有改变你的方法(你没有提供完整的查询),但你正在进行不同维度的连接 - 类别,图像和选项。这为每个产品创建了一个笛卡尔积,这通常不是这种查询的最佳方法。

答案 1 :(得分:1)

解决方案中无需LEFT JOIN

SELECT DISTINCT p.id AS id
FROM products p
JOIN product_options po ON p.id = po.product_id
WHERE p.product_active = 1
      AND po.option_id IN (1, 2, 3)
GROUP BY p.id
HAVING COUNT(po.option_id) = 3

我的解决方案只保留查找具有指定选项的产品所需的表格。

如果您希望产品具有恰好此选项而没有其他产品,则可以使用NOT EXISTS

SELECT DISTINCT p.id AS id
FROM products p
JOIN product_options po ON p.id = po.product_id
WHERE p.product_active = 1 AND 
      po.option_id IN (1, 2, 3) and
      NOT EXISTS (
        SELECT 1
        FROM product_options po2
        WHERE p.id = po2.product_id and po2.option_id NOT IN (1, 2, 3)
      )
GROUP BY p.id
HAVING COUNT(po.option_id) = 3

如果您想选择符合其他条件的产品(如产品类别等),请在IN子句中使用WHERE。此方法可避免生成重复po.option_id,即使DISTINCT中没有COUNT,外部查询仍可正常工作。

SELECT DISTINCT p.id AS id
FROM products p
JOIN product_options po ON p.id = po.product_id
WHERE p.product_active = 1 AND 
      po.option_id IN (1, 2, 3) AND
      -- use the following IN predicate to select products with specific features without introducing duplicates in your query
      p.id IN (
         select product_id FROM product_categories WHERE <your_condition>
      )
GROUP BY p.id
HAVING COUNT(po.option_id) = 3

答案 2 :(得分:0)

您选择带有图像列表的产品。类似的东西:

select products.*, group_concat(product_images.id)

此外,产品必须满足各种选择。这是属于WHERE子句的标准。

select
  p.*,
  (select group_concat(image) from product_images i where i.product_id = p.id) as images
from products p
where product_active = 1
and id in
(
  select product_id
  from product_options
  where option_id in (1,3,55,97)
  group by product_id
  having count(*) = 4 -- four options in this example
);

答案 3 :(得分:0)

谢谢你们,我已经成功归还了我想要的东西。

现在我对过滤产品的分页查询存在问题。

最终搜索查询:

SELECT DISTINCT p.id AS id,
                main_price,
                promotion_price,
                NEW,
                sale,
                recommended,
                COUNT(pi.filename) AS image_count,
                GROUP_CONCAT(DISTINCT pi.filename
                             ORDER BY pi.main_image DESC, pi.id ASC) AS images,
                name_sr,
                uri_sr,
                description_sr
FROM products p
LEFT JOIN product_categories pc ON p.id = pc.product_id
LEFT JOIN product_images pi ON p.id = pi.product_id
LEFT JOIN product_options po ON p.id = po.product_id
WHERE p.product_active = 1
  AND po.option_id IN(1)
  AND p.main_price BETWEEN 5250.00 AND 14000.00
GROUP BY id
HAVING COUNT(DISTINCT po.option_id) = 1
ORDER BY id DESC
LIMIT 0,
      12

分页查询是这样的,我根据新的过滤查询修改了它:

SELECT COUNT(DISTINCT p.id) AS number
FROM products p
LEFT JOIN product_categories pc ON p.id = pc.product_id
LEFT JOIN product_images pi ON p.id = pi.product_id
LEFT JOIN product_options po ON p.id = po.product_id
WHERE p.product_active = 1
  AND po.option_id IN(1)
  AND p.main_price BETWEEN 5250.00 AND 14000.00
GROUP BY(p.id)
HAVING COUNT(DISTINCT po.option_id) = 1

如果我在SELECT COUNT中省略了DISTINCT,我没有得到过滤分页,如果我设置了DISTINCT,我会得到对应于分页的行数。我想我可以使用子查询为所有这些添加另一个计数(*),但不确定是否可行,以及是否有更有效和更优雅的方法来执行此操作。