Join query with only columns that have all values in `in` clause

*爱你&永不变心* 提交于 2019-11-29 16:02:24

You can do this by aggregating the IDs into an array and then compare that with the list of intended IDs:

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'];

The above assumes that venue.id is declared as the primary key (because of the group by).

You don't really need to hardcode the IDs in the query if you would like to just pass the amenity names:

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'));

Online example: https://rextester.com/FNNVXO34389

I think you are looking for

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
               )
      );

That should work independent of how many amenities there are.

You can add conditions in the places I indicated to limit the query to only a certain subset of amenities or venues.

Is this what you are looking for?

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

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
)

Right:

  • The essential part of this is to only return venue_ids for the outer select.
  • You need group by to facilitate using having.
  • Having is an aggregate form of where clause.
  • The having 2 makes sure both aaa AND bbb are present to return a venue id in the inner select, rather than the default OR.
  • count(1) - you can use column numbers instead of asterisk or column name too for aggregate functions.

Proof the code works:

https://rextester.com/TXQB38528

I made some minor tweaks and added this version too.

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)
);

That way you aren't constrained by maintaining counts of amenities. You can see it here. https://rextester.com/TKRF28879

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!