仅使用在 `in` 子句中具有所有值的列连接查询

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

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

注意:所有 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'        |

我正在尝试向 return 至少拥有所有通过 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');

这 return 是所有具有 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';

这return没什么。我正在尝试编写一个查询,其中如果 amenity_ids aaabbb 仅在会场 aaa 中传递 returned 因为它是唯一具有与两种便利设施的关系。此外,便利设施的数量在查询之间是动态的。

这是您要找的吗?

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

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

您可以在我指定的地方添加条件,以将查询限制为仅特定的设施或场所子集。

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
)

右:

  • 这个的本质部分是只有 return venue_ids 用于外部 select.
  • 您需要分组依据以方便使用 having。
  • Having 是 where 子句的聚合形式。
  • 有 2 确保 aaa AND bbb 都存在于 return 内部 select 中的场地 ID,而不是默认
  • 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

您可以将 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