小编典典

选择多对多关系中的匹配子集

sql

假设我与用户和项目之间存在多对多关系:一个用户可能属于多个项目,而一个项目可能具有多个用户。此关系在表中编码user_projects

create table user_projects
(
proj_id int references projs(id) not null,
user_id int references users(id) not null,
primary key (proj_id, user_id)
);

这是我的问题:给定一组用户(user1,user2,…),我想选择给定用户集为其所有用户的子集的所有项目。

例如,如果我在下面插入数据,然后询问用户1和2的所有项目,则查询应仅返回项目1。

insert into user_projects values (1, 1);
insert into user_projects values (1, 2);
insert into user_projects values (1, 3);
insert into user_projects values (2, 1);
insert into user_projects values (2, 3);

(如果最好的解决方案恰巧是非标准的,那么我使用的是PostgreSQL。)

编辑:
为澄清起见,应将用户集解释为对要返回的项目列表的约束。集合{u1,u2}表示项目列表应仅包括那些至少具有用户u1和u2的项目;集合{u1}表示应返回至少具有用户u1的所有项目,而在有限的情况下,空集合表示应返回
所有 项目。


阅读 188

收藏
2021-04-28

共1个答案

小编典典

Select project_ID 
from user_projects
where user_ID in (1,2)
group by project_ID
Having count(*) = 2

您知道您有2个用户,您知道他们将是唯一的(主键),因此您知道如果有2条记录,那么对于同一项目,则是您想要的。

您的问题表明您收到了GIVEN个用户,因此您知道哪些用户以及有多少用户。上面的SQL可以更新为接受这些已知参数的参数,因此保持动态,而不仅限于2个用户。

where user_ID in (userlist)
having count(*) = (cntuserList)

-----------处理用户为空时的情况-----

Select P.project_ID 
from Projects P
LEFT JOIN user_projects UP
where (UP.user_ID in (1,2) OR UP.USER_ID is null)
group by project_ID
Having count(*) = 2

这就是它的作用。它返回所有项目,并且如果有与该项目相关的用户,它将对其进行标识。如果您的集合包含用户,则返回的项目列表将由该集合过滤,以确保整个集合都通过Haveing子句位于项目中。

如果集合为空,则LEFT联接以及userID为null语句将保留未列出用户的项目,无论集合是否为空。hading子句会将集合进一步减少为您在集合中定义的用户数量,或者为0表示返回所有未分配用户的项目。

我们还没有讨论的另一种极端情况是,如果一个项目包含的用户数量超过了您在集合中定义的数量,那么该怎么办。目前将退还该项目;但我不是很肯定那是你想要的。

在旁注感谢您让我思考。我不再需要太多的代码了。这就是为什么我不时在这里拖钓看看我是否可以帮忙!

2021-04-28