【问题标题】:how to filter Grouped results to match more than one value?如何过滤分组结果以匹配多个值?
【发布时间】:2021-06-18 18:52:40
【问题描述】:

-问题本身可能无法很好地描述问题,但我不确定如何呈现它

我已经实现了this设计 ,所以我有下表。

 +---------------+     +-------------------+
 | PRODUCTS      |-----< PRODUCT_VARIANTS  |
 +---------------+     +-------------------+
 | #product_id   |     | #product_id       |
 |  product_name |     | #variant_id       |
 +---------------+     |  price            |
         |             +-------------------+
         |                       |
+--------^--------+     +--------^--------+
| PRODUCT_OPTIONS |-----< VARIANT_VALUES  |
+-----------------+     +-----------------+
| #product_id     |     | #product_id     |
| #option_id      |     | #variant_id     |
+--------v--------+     | #option_id      |
         |              |  option_value_id|
+-----------------+     +--------v--------+
| OPTIONS         |              |
+-----------------+              |
| #option_id      |              |
|  option_name    |              |
+-----------------+              |
         |                       |
 +-------^---------+             |
 | OPTION_VALUES   |-------------+
 +-----------------+
 | #option_id      |
 | #option_value_id|
 |  value_name     |
 +-----------------+

带“#”的字段是主键

我想让用户使用option_values 过滤产品。假设我有以下产品变体shirtA(product) size(option) small(option_value), color(option) green(option_value),并且用户想要过滤所有具有small and red 衬衫的产品,因此不应包括以前的产品变体。

我试过这样做

SELECT p.product_id, p.title, p.description FROM product_variants pv
JOIN products p ON pv.product_id = p .product_id
JOIN variant_values vv ON vv.product_variant_id = pv.product_variant_id
WHERE vv.option_value_id IN (6, 10)
GROUP BY (p.product_id, p.title, p.description);

但如您所见,它包括拥有option_value_id 610 而不是两者兼有的产品,因此如何删除没有两个 option_values 的产品不止一个?


更新 1

澄清我需要做的是:强制所有选择的产品具有用户发送的所有选项值 ID(以数组的形式)WHERE vv.option_value_id IN (6, 10)。这个想法就像在大多数电子商务网站中,当用户过滤他/她的偏好option_values时,用户可能想要搜索所有large (let's say that is option_value_id "6") and red (option_value_id "10") and maybe green...的产品等等。所以我不希望任何只有“红色”颜色但没有“大”尺寸的产品被退回,如果用户在过滤器中添加了另一种颜色说“绿色”颜色,那么所有退回的产品都应该匹配所有三个过滤器(具有绿色和红色以及大尺寸)。

【问题讨论】:

  • ...HAVING COUNT(DISTINCT option_value_id) = 2
  • 如果您想同时将“in”更改为“and”,例如 WHERE vv.option_value_id = 6 和 vv.option_value_id =10
  • 将选项建模为产品的列。
  • WHERE vv.option_value_id = 6 and vv.option_value_id =10 这个不行,单列的值不能同时是6和10
  • 谢谢@CountZukula 我错了。我添加了没有深入检查的评论。

标签: sql postgresql relational-division


【解决方案1】:

由于您已经有一组预定义的 id,也许您可​​以在此处使用数组。

SELECT p.product_id, p.title, p.description FROM product_variants pv
JOIN products p ON pv.product_id = p .product_id
JOIN variant_values vv ON vv.product_variant_id = pv.product_variant_id
WHERE vv.option_value_id IN (6, 10)
GROUP BY (p.product_id, p.title, p.description)
HAVING array_agg(distinct vv.option_value_id order by vv.option_value_id) = array[6,10];

【讨论】:

  • array(6,10) 是无效的数组文字。你必须使用array[6,10]
  • 它抱怨它无法将 bigInt 转换为 Int 所以我添加了这个 HAVING array_agg(distinct vv.option_value_id) = ARRAY[6::bigint,10::bigint]; 但我不确定这里发生了什么你能澄清一下你为什么使用array_agg and distinct
  • @OmarAbdelhady:你也可以使用array[6,10]::bigint[]
  • @OmarAbdelHady 我仍然稍微修改了答案。我将尝试解释array_agg+distinct。对于每个组,执行distinct option_value_id order by option_value_id 可确保您获得一个不错的 id 列表,已排序,永不重复。这个列表需要聚合成单个值,否则不能用作过滤器。这就是array_agg 所做的:它将列表提炼成一个array。您可以使用 = array[6,10] 比较该数组。
  • 不过,@gordon-linoff 的回答似乎更简洁、更高效且同样正确。
【解决方案2】:

你想要一个having 子句。你的问题不清楚。但如果你想要两者,那么使用:

SELECT p.product_id, p.title, p.description
FROM product_variants pv JOIN
     products p 
     ON pv.product_id = p .product_id JOIN
     variant_values vv
     ON vv.product_variant_id = pv.product_variant_id
WHERE vv.option_value_id IN (6, 10)
GROUP BY p.product_id, p.title, p.description
HAVING COUNT(DISTINCT option_value_id) = 2;

如果您只想要一个,您可以将2 更改为1

【讨论】:

  • 谢谢。我更新了问题,请您看一下以确认。
【解决方案3】:

如果您可以选择动态 SQL 构建,您可以使用 INTERSECT 过滤产品变体:

SELECT p.product_id, p.title, p.description 
FROM product_variants pv
  JOIN products p ON pv.product_id = p .product_id
  JOIN (SELECT variant_id
        FROM variant_values
        WHERE (option_value_id = 6)
        INTERSECT 
        SELECT variant_id
        FROM variant_values
        WHERE (option_value_id = 10)
    ) as vv ON vv.product_variant_id = pv.product_variant_id;

如果您计划大量产品变体及其选项,这可能不是最佳查询,但查询仍然足够简单,可以尝试。

【讨论】:

    猜你喜欢
    • 2021-11-11
    • 2018-03-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-23
    • 2016-10-11
    • 1970-01-01
    • 2015-01-01
    • 1970-01-01
    相关资源
    最近更新 更多