仅使用具有“in”子句中所有值的列加入查询
Posted
技术标签:
【中文标题】仅使用具有“in”子句中所有值的列加入查询【英文标题】:Join query with only columns that have all values in `in` clause 【发布时间】:2019-02-08 09:37:47 【问题描述】:我正在为我的网站创建一个简单的过滤系统。我在场地和设施之间有多对多的关系。这是我的桌子。
注意:所有 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 aaa
和 bbb
。
当传入的便利设施 ID 为 aaa
和 bbb
时,我正在寻找输出。
| 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 aaa
或 bbb
的场所
| 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 aaa
和 bbb
在仅场地 aaa
中传递,则返回,因为它是唯一与这两个设施有关系的场地。此外,便利设施的数量在查询之间是动态的。
【问题讨论】:
分享你的输出 有了那个样本表数据,预期的结果是什么? (顺便说一句,'aaa' 很容易阅读,但 '0cbe0352-89b6-4ed5-8a4e-b8127d32b5b3' 不是。) 在 WHERE IN(设施 ID 列表)中执行。 GROUP BY, HAVING COUNT = 便利设施数量。 @jarlh 你能用这个例子分享答案吗? @user10457989 现在人们已经为您提供了可行的解决方案,请务必接受答案。 【参考方案1】:您可以通过将 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
【讨论】:
我需要为 uuid 进行类型转换吗? @user10457989:对于数组字面量是:array['...', '...']::uuid[]
@user10457989:我已经更新了在线示例以使用 UUID【参考方案2】:
我想你正在寻找
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
)
);
这应该与有多少便利设施无关。
您可以在我指出的地方添加条件,以将查询限制为仅限设施或场所的某个子集。
【讨论】:
我在哪里传递amenity_ids
?
我已经扩展了答案以显示可以添加条件的位置。
我试过这个查询。与没有 v.name in
子句的查询相同。 SELECT v.* FROM venues v WHERE NOT EXISTS ( SELECT 1 FROM amenities AS a WHERE a.name IN ('0cbe0352-89b6-4ed5-8a4e-b8127d32b5b3', '80623c5b-f794-4c17-913a-88b8ac147f1c') AND NOT EXISTS ( SELECT 1 FROM amenity_venue AS av WHERE av.venue_id = v.id AND av.amenity_id = a.id ) );
这返回了 db 中的所有场地。
如果您传递的是 UUID 而不是名称,请使用 a.id IN ...
而不是 a.name IN ...
。【参考方案3】:
这就是你要找的吗?
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
【讨论】:
感谢您的回答;但是,这仍然会返回所有具有“aaa”或“bbb”的场所。这个查询的一个升级是它“分组”了场地。 我相应地更新了我的查询。你能再检查一下吗? 现在不返回场地。【参考方案4】: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
)
对:
其中的关键部分是只为外部选择返回venue_ids。 您需要 group by 以方便使用 have。 Having 是 where 子句的聚合形式。 拥有 2 确保 aaa AND 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
【讨论】:
这将返回所有与便利设施aaa
或bbb
有关系的场所
count(distinct venue_id ) = 2
我已经相应地修改了 SQL
查询还有更多内容吗?我收到此错误ERROR: syntax error at or near ")" LINE 11: )
Sorry drop second ) 已修复以上是关于仅使用具有“in”子句中所有值的列加入查询的主要内容,如果未能解决你的问题,请参考以下文章
如何为 where 子句中的列编写具有不连续值的 Cassandra 查询