选择具有满足某些约束的多个子关系的所有行

时间:2018-05-08 14:45:06

标签: sql postgresql

假设我有一个包含一定重量水果的篮子清单:

Table baskets
(id, name)
----------------
1, 'apples, oranges and more'
2, 'apples and small oranges'
3, 'apples and bananas'
4, 'only oranges'
5, 'empty'

Table basket_fruits
(id, basket, fruit, weight)
----------------
1, 1, 'apple', 2
2, 1, 'apple', 3
3, 1, 'orange', 2
4, 1, 'banana', 2
5, 2, 'apple', 2
6, 2, 'orange', 1
7, 3, 'apple', 2
8, 3, 'banana', 2
9, 4, 'orange', 2

SQL Fiddle with this data

我很难为这两种情况提出合理有效的查询:

  1. 我想获取包含至少一个apple和至少一个orange的所有篮子,每个篮子都超过给定的重量。因此weight >= 2的预期结果是

    1, 'apples, oranges and more'
    

    weight >= 1

    1, 'apples, oranges and more'
    2, 'apples and small oranges'
    
  2. 我想获取所有包含没有水果超过给定重量的篮子。所以对于weight >= 2,我希望

    5, 'empty'
    

    weight >= 3它应该返回

    2, 'apples and small oranges'
    3, 'apples and bananas'
    4, 'only oranges'
    5, 'empty'
    
  3. 权重约束只是“每个子关系必须满足某些约束”的占位符。在实践中,我们需要按日期范围,状态等限制子关系,但我不想让这个例子进一步复杂化。

    (我正在使用postgresql,以防解决方案需要特定于数据库。)

3 个答案:

答案 0 :(得分:1)

我强烈建议您使用group byhaving来实现此目的。

对于您的第一个问题,此查询应该有效:

SELECT b.name
FROM baskets b INNER JOIN
     basket_fruits bf
     ON b.id = bf.basket
GROUP BY b.name
HAVING SUM( (bf.fruit = 'apple' AND bf.weight >= 2)::int ) > 0 AND
       SUM( (bf.fruit = 'orange' AND bf.weight >= 2)::int ) > 0 ;

第二个稍微复杂一点,因为没有行。但left joincoalesce()足以让您以相同的格式表达:

SELECT b.name
FROM baskets b LEFT JOIN
     basket_fruits bf
     ON b.id = bf.basket
GROUP BY b.name
HAVING SUM( (COALESCE(bf.weight, 0) >= 2)::int ) = 0  

答案 1 :(得分:1)

到目前为止,这是我的解决方案:

  1. 所有包含水果weight >= 2的篮子(感谢Gordon Linoff的建议):

    SELECT b.* FROM baskets b
    INNER JOIN (
        SELECT basket FROM basket_fruits
        WHERE weight >= 2
        GROUP BY basket
        HAVING SUM((fruit = 'apple')::int) > 0 AND SUM((fruit = 'orange')::int) > 0
    ) bf ON b.id = bf.basket
    

    SQL-Fiddle

  2. 所有没有水果的篮子weight >= 2

    SELECT b.* FROM baskets b
    LEFT JOIN (
        SELECT basket, fruit FROM basket_fruits
        WHERE weight >= 2
    ) bf ON b.id = bf.basket
    WHERE fruit IS NULL
    

    SQL-Fiddle

  3. 如果有人有更高效的想法,我很乐意听到他们的意见。

答案 2 :(得分:0)

SELECT b.name
    FROM baskets b
       INNER JOIN basket_fruits f
           ON b.id = f.basket
GROUP BY b.name
HAVING SUM(f.wage) >= 3
OR b.id = 5

这是你所期望的吗?