PostgreSQL条件连接和析取在一个查询中

时间:2013-09-11 16:23:27

标签: sql postgresql relational-division sql-match-all

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

情况描述

  1. 有一家商店有产品。
  2. 产品可能具有或不具备功能。
  3. 客户寻找产品的特定功能,这意味着填写表单并发送一系列功能ID。
  4. 在数据库中,每个功能只属于一组功能。
  5. 如果其中一个功能与客户提交的任何功能相匹配,则第一个组(分离属性为true,称为“OR”)允许显示产品。
    示例:选择形状:圆形,方形,三角形显示圆形或正方形或三角形的产品。
  6. 第二组(拆分属性为false,称为“AND”)仅在产品具有客户提交的所有功能时才允许显示产品。
    示例:选择颜色:红色,绿色,蓝色显示红色,绿色和蓝色的产品。
  7. 测试环境

    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
    

    “和”查询
    无法使用此查询,因为不知道disjunction为false的要素数。

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

1 个答案:

答案 0 :(得分: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_arrNULL改为使用_my_arr IS NULL

  • 由于operator precedence ANDOR之前绑定,因此不需要括号。但它们可能会提高可读性。

  • 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  = '{}';

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

“AND”案例

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

可能是:

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 demo.