一次查询中的PostgreSQL条件连接和条件分离

一次查询中的PostgreSQL条件连接和条件分离,sql,postgresql,relational-division,sql-match-all,Sql,Postgresql,Relational Division,Sql Match All,如何创建一个查询来选择给定特征的产品,其中特征语句由“and”或“or”条件组成,取决于它们所属的组 情况描述 有一家商店出售各种商品 产品可能有功能,也可能没有 客户查找产品的特定功能,这意味着填写表单并发送一系列功能ID 在数据库中,每个特征仅属于一组特征 第一组(析取属性为true,称为“OR”)允许在其中一个功能与客户提交的任何功能匹配时显示产品。 示例:选择形状:圆形、正方形、三角形显示圆形、正方形或三角形的产品 第二组(析取属性为false,称为“AND”)仅当产品具有客户提交的所有

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

情况描述

  • 有一家商店出售各种商品
  • 产品可能有功能,也可能没有
  • 客户查找产品的特定功能,这意味着填写表单并发送一系列功能ID
  • 在数据库中,每个特征仅属于一组特征
  • 第一组(析取属性为true,称为“OR”)允许在其中一个功能与客户提交的任何功能匹配时显示产品。
    示例:选择形状:圆形、正方形、三角形显示圆形、正方形或三角形的产品
  • 第二组(析取属性为false,称为“AND”)仅当产品具有客户提交的所有功能时才允许显示产品。
    示例:选择颜色:红色、绿色、蓝色显示红色、绿色和蓝色的产品
  • 测试环境

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

    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
    
    “和”查询
    无法使用此查询,因为析取为false的要素数量未知

    SELECT product_id FROM product_features
    WHERE feature_id IN (43, 53, 63)
    GROUP BY product_id
    HAVING COUNT(DISTINCT feature_id) = 3
    
    “或”案例 更简单、更快:

    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  = '{}';
    
    。。。其中
    \u my_arr
    可以是
    '{11,12,13}'::int[]
    '{}'::int[]
    。如果
    \u我的arr
    将为
    NULL
    请使用
    \u我的arr为NULL

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

    • DISTINCT
      分组依据
      。。这两种都很好

    • 和fg.析取
      。。由于这是一种布尔类型,因此可以缩短语法

    • 联接通常比子句中的另一个
      更快

    • 使用
      只是一种与您的(有用的!)命名约定配合使用的符号快捷方式

    或者,更快(适用于多个功能)-并且更易于拆分案例:

    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  = '{}';
    
    我宁愿在你的应用程序中根据输入(有功能/无功能)拆分案例。与第二种形式无关

    “和”案例 这是关系划分的经典案例。我们已经收集了一整套查询技术来处理这个相关问题:

    可以是:

    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
    ...
    
    我忽略了示例中的
    非特征组。析取
    ,因为它也不在问题中。如果需要,请添加它。
    在生成查询之前,我会选择有效的功能\u id


    “除了那些没有功能的产品外,它也可以工作。”-如果它们没有功能,不应该通过“或”查询将其删除吗?根据您的描述,“没有功能的产品”将永远不会出现在您的任何一个用例中。@PinnyM我应该使用不同的表达式,并更好地描述它。当客户选择至少一个特征时,查询工作正常。那么“或”查询确实应该消除没有特性的产品。但是,如果客户没有选择任何功能并查找产品,则应显示所有产品。在这种情况下,使用“或”查询不能按需要工作。请记住,WHERE in子句中设置的功能ID不仅是“disjunction is true”类型,查询会动态过滤它们,因此可能无法使用这些ID。我的印象是,我没有足够清楚地表达自己。您的“或”查询看起来比我的好得多,我非常感谢您的提示,但它仍然不能按需要工作。WHERE IN子句包含客户选择的一组功能ID。如果这些功能都不属于具有“disjunction is true”属性的组,或者客户没有选择任何功能,则查询不返回产品,但在这种情况下,它应该返回所有产品。完美”,或者“查询完成任务”。“和”查询似乎非常快,但维护起来有点困难,特别是如果有数百个功能的话。此外,混合特征id集在一个数组中一起发送,因此不知道哪个特征id属于“或”或“和”查询。在本例中,这些属于“析取为假”组的特征必须以某种方式加以选择。@rafis:我在回答你的问题,我可能会补充一些细节。应该引起公众的兴趣。我不是做你所有的工作。如果你遇到了另一个障碍,打开一个新问题,清楚地定义一个问题。