Выберите все строки, которые имеют несколько под-отношений, удовлетворяющих некоторым ограничениям - PullRequest
0 голосов
/ 08 мая 2018

Допустим, у меня есть список корзин, которые могут содержать фрукты с определенным весом:

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 с этими данными

Я изо всех сил пытаюсь придумать достаточно эффективных запросов для этих двух сценариев:

  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'
    

Весовое ограничение является просто заполнителем для "каждого подотношения должно соответствовать определенным ограничениям". На практике нам нужно ограничить подотношение диапазоном дат, статусом и т. Д., Но я не хотел бы еще больше усложнять пример.

(я использую postgresql, если решение должно быть привязано к базе данных.)

Ответы [ 3 ]

0 голосов
/ 08 мая 2018

Я настоятельно рекомендую использовать group by и having для этой цели.

Для вашего первого вопроса этот запрос должен работать:

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 join и coalesce(), чтобы вы могли выразить их в одном формате:

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  
0 голосов
/ 08 мая 2018

Вот мои решения на данный момент:

  1. Все корзины с фруктами weight >= 2 (благодаря предложениям Гордона Линоффа):

    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

Если у кого-то есть более эффективные идеи, я хотел бы услышать их.

0 голосов
/ 08 мая 2018
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

Это то, что вы ожидали?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...