2

如何创建一个查询来选择给定特征的产品,其中特征语句由“和”或“或”条件形成,具体取决于它们所属的组?

情况描述

  1. 有一个产品商店。
  2. 产品可能有功能,也可能没有。
  3. 客户寻找产品的特定功能,这意味着填写表格并发送一系列功能 ID。
  4. 在数据库中,每个特征只属于一组特征。
  5. 第一组(析取属性为真,称为“OR”)允许在其中一个特征与客户提交的任何特征匹配时显示产品。
    示例:选择形状:圆形、方形、三角形显示圆形或正方形或三角形的产品。
  6. 第二组(析取属性为假,称为“AND”)仅在产品具有客户提交的所有特征时才允许显示产品。
    示例:选择颜色:红、绿、蓝显示产品为红绿蓝。

测试环境

http://sqlfiddle.com/#!12/f4db7

“或”查询
除了那些没有功能的产品外,它可以工作。

SELECT product_id 
FROM product_features 
WHERE product_features.feature_id IN ( 
    SELECT feature_id FROM features 
    LEFT JOIN feature_groups 
        ON features.feature_group_id = feature_groups.feature_group_id 
    WHERE feature_id IN (11, 12, 13) AND feature_groups.disjunction = TRUE 
)
GROUP BY product_id

“AND”查询
无法使用此查询,因为不知道析取为假的特征数量。

SELECT product_id FROM product_features
WHERE feature_id IN (43, 53, 63)
GROUP BY product_id
HAVING COUNT(DISTINCT feature_id) = 3
4

1 回答 1

1

“或”的情况

更简单更快:

SELECT DISTINCT pf.product_id 
FROM   product_features    pf
LEFT   JOIN features       f  USING (feature_id)
LEFT   JOIN feature_groups fg USING (feature_group_id)
WHERE (f.feature_id = ANY (_my_arr) 
AND    fg.disjunction)
OR     _my_arr  = '{}';

... 哪里_my_arr可以 '{11, 12, 13}'::int[]'{}'::int[]。如果_my_arr将改为NULL使用_my_arr IS NULL

  • 由于运算符优先级 AND绑定之前OR并且不需要括号。不过,它们可能会提高可读性。

  • DISTINCTGROUP BY.. 任何一个都很好。

  • AND fg.disjunction.. 由于这是一个布尔类型,您可以缩短语法。

  • JOIN 通常比另一个IN子句快。

  • USING只是一个符号快捷方式,适用于您的(有用的!)命名约定。

或者,甚至更快(对于超过 1 个功能) - 并且更容易拆分案例:

SELECT product_id
FROM   products p
WHERE  EXISTS (
   SELECT 1
   FROM   product_features pf
   JOIN   features         f  USING (feature_id)
   JOIN   feature_groups   fg USING (feature_group_id)
   WHERE  pf.product_id = p.product_id
   AND    f.feature_id = ANY (_my_arr)
   AND    fg.disjunction
   )
OR     _my_arr  = '{}';

我宁愿根据输入(有功能/无功能)在您的应用程序中拆分案例。与第二种形式无关。

“与”案例

这是关系划分的经典案例。在这个相关问题下,我们已经组装了一整套查询技术来处理它:
How to filter SQL results in a has-many-through relationship

可能:

SELECT product_id
FROM   product_features p1
JOIN   product_features p2 USING (product_id)
JOIN   product_features p3 USING (product_id)
...
WHERE  p1.feature_id = 43
AND    p2.feature_id = 53
AND    p3.feature_id = 63
...

NOT feature_groups.disjunction在这个例子中忽略了,因为它也不是问题。如果需要,请添加它。
在构建查询之前,我会选择有效的 feature_id。

-> SQLfiddle 演示。

于 2013-09-11T16:56:58.890 回答