连接查询中仅包含in子句中所有值的列

时间:2019-02-08 09:37:47

标签: sql postgresql relational-division

我正在为我的网站创建一个简单的过滤系统。场地和便利设施之间存在许多关系。这是我的桌子。

注意:所有ID均为uuid。让它们简明扼要

地点:

| id    |      name      |
_________________________
| 'aaa' |  'first venue' |
| 'bbb' | 'second venue' |
| 'ccc' | 'third venue'  |

设施:

| id    |      name        |
___________________________
| 'aaa' |  'first amenity' |
| 'bbb' | 'second amenity' |
| 'ccc' | 'third amenity'  |

amenity_venue:

| amenity_id  |    venue_id  |
______________________________
| 'aaa'       |  'aaa'       |
| 'bbb'       | 'aaa'        |
| 'ccc'       | 'aaa'        |
| 'aaa'       | 'bbb'        |
| 'bbb'       | 'ccc'        |

我正在尝试编写查询以返回至少包含所有已通过amenity_ids传递的场所。例如,传递amenity_ids aaabbb

输出,我正在寻找何时传入的设施ID为aaabbb

| id    |      name      |
_________________________
| 'aaa' |  'first venue' |

最初我尝试了此查询

select * from venues 
INNER JOIN amenity_venue ON amenity_venue.venue_id = venues.id
where amenity_id in ('aaa', 'bbb');

这将返回所有amenity_id为aaabbb的场所

| id    |      name      |
_________________________
| 'aaa' |  'first venue' |
| 'bbb' | 'second venue' |
| 'ccc' | 'third venue'  |

所以我天真地尝试了

select * from venues 
INNER JOIN amenity_venue ON amenity_venue.venue_id = venues.id
where amenity_id = 'aaa'
  and amenity_id = 'bbb';

什么都不返回。我正在尝试编写一个查询,其中返回amenity_ids aaabbb仅在场所aaa中传递,因为它是唯一与这两种便利设施都有关系的场所。而且,查询之间的便利设施数量是动态的。

4 个答案:

答案 0 :(得分:0)

这是您要找的东西吗?

select * from venues 
where  exists (
    select venue_id from amenity_venue 
    where venues.id = amenity_venue.venue_id and amenity_id in ('aaa', 'bbb')
    group by venue_id
    having count(*) = 2
  )

Working Solution

答案 1 :(得分:0)

我认为您正在寻找

SELECT v.*
FROM venues v
WHERE v.name IN (/* list of venues names */)
  AND NOT EXISTS (
         SELECT 1
         FROM amenities AS a
         WHERE a.name IN (/* list of amenity names */)
           AND NOT EXISTS (
                  SELECT 1
                  FROM amenity_venue AS av
                  WHERE av.venut_id = v.id
                    AND av.amenity_id = a.id
               )
      );

这应该独立于有多少便利设施而工作。

您可以在我指示的位置添加条件,以将查询限制为仅设施或场所的某些子集。

答案 2 :(得分:0)

select * from venues where id in(
 select venue_id
 from amenity_venue
 where amenity_id in('aaa','bbb')
 group by venue_id
 having count(1) = 2
)

右:

  • 其中必不可少的部分是仅返回外部选择的场地ID。
  • 您需要分组依据以方便使用have。
  • 具有是where子句的聚合形式。
  • 具有2的a确保同时存在aaa bbb以在内部选择中返回场地ID,而不是默认的 OR
  • count(1)-对于聚合函数,您也可以使用列号而不是星号或列名。

证明代码有效:

https://rextester.com/TXQB38528

我做了一些小调整,并添加了此版本。

with amenities as (select 'aaa' as amenity_id UNION select 'bbb'),
ac as (select count(amenity_id) as tally from amenities)
select * from venues where id in(
 select venue_id
 from amenity_venue
 where amenity_id in(select amenity_id from amenities)
 group by venue_id
 having count(1) = (select tally from ac)
);

这样一来,您就不会受到维护各种便利设施的束缚。 你可以在这里看到它。 https://rextester.com/TKRF28879

答案 3 :(得分:0)

您可以通过将ID汇总到一个数组中,然后将其与预期ID列表进行比较来实现此目的:

select v.*
from venues v
  join amenity_venue av ON av.venue_id = v.id
group by v.id
having array_agg(av.amenity_id) @> array['aaa', 'bbb'];

以上假设venue.id被声明为主键(由于group by)。

如果您只想传递便利设施名称,则实际上不需要对查询中的ID进行硬编码:

select v.*
from venues v
  join amenity_venue av ON av.venue_id = v.id
group by v.id
having array_agg(av.amenity_id) @> array(select id 
                                         from amenities 
                                         where name in ('first amenity', 'second amenity'));

在线示例:https://rextester.com/FNNVXO34389