Ray*_*Ray 5 sql postgresql relational-division
我有一个PostgreSQL数据库,用于将用户存储在users表中,并将对话存储在conversation表中.由于每个用户都可以参与多个对话,并且每个对话都可能涉及多个用户,因此我有一个conversation_user链接表来跟踪哪些用户参与每个对话:
# conversation_user
id | conversation_id | user_id
----+------------------+--------
1 | 1 | 32
2 | 1 | 3
3 | 2 | 32
4 | 2 | 3
5 | 2 | 4
Run Code Online (Sandbox Code Playgroud)
在上表中,用户32只与用户3进行一次对话,另一次与3和用户4进行对话.如何编写一个查询,表明只有用户32和用户3之间有对话?
我尝试过以下方法:
SELECT conversation_id AS cid,
user_id
FROM conversation_user
GROUP BY cid HAVING count(*) = 2
AND (user_id = 32
OR user_id = 3);
SELECT conversation_id AS cid,
user_id
FROM conversation_user
GROUP BY (cid HAVING count(*) = 2
AND (user_id = 32
OR user_id = 3));
SELECT conversation_id AS cid,
user_id
FROM conversation_user
WHERE (user_id = 32)
OR (user_id = 3)
GROUP BY cid HAVING count(*) = 2;
Run Code Online (Sandbox Code Playgroud)
这些查询会抛出一个错误,指出user_id必须出现在GROUP BY子句中或用于聚合函数.将它们置于聚合函数(例如MIN或MAX)中听起来不合适.我认为我的前两次尝试是将它们放入GROUP BY条款中.
我究竟做错了什么?
这是关系划分的情况。我们在这个相关问题下收集了一系列技术:
特殊的困难是排除额外的用户。基本上有4种技术。
我建议LEFT JOIN/ IS NULL:
SELECT cu1.conversation_id
FROM conversation_user cu1
JOIN conversation_user cu2 USING (conversation_id)
LEFT JOIN conversation_user cu3 ON cu3.conversation_id = cu1.conversation_id
AND cu3.user_id NOT IN (3,32)
WHERE cu1.user_id = 32
AND cu2.user_id = 3
AND cu3.conversation_id IS NULL;
Run Code Online (Sandbox Code Playgroud)
或者NOT EXISTS:
SELECT cu1.conversation_id
FROM conversation_user cu1
JOIN conversation_user cu2 USING (conversation_id)
WHERE cu1.user_id = 32
AND cu2.user_id = 3
AND NOT EXISTS (
SELECT 1
FROM conversation_user cu3
WHERE cu3.conversation_id = cu1.conversation_id
AND cu3.user_id NOT IN (3,32)
);
Run Code Online (Sandbox Code Playgroud)
这两个查询都不依赖于 的UNIQUE约束(conversation_id, user_id),该约束可能存在也可能不存在。user_id这意味着,如果同一对话多次列出 32(或 3),则查询甚至可以工作。但是,您会在结果中得到重复的行,并且需要应用DISTINCTor GROUP BY。
唯一的条件是您制定的条件:
...显示只有用户 32 和用户 3 之间存在对话的查询?
您在评论中链接的查询将不起作用。您忘记排除其他参与者。应该是这样的:
SELECT * -- or whatever you want to return
FROM conversation_user cu1
WHERE cu1.user_id = 32
AND EXISTS (
SELECT 1
FROM conversation_user cu2
WHERE cu2.conversation_id = cu1.conversation_id
AND cu2.user_id = 3
)
AND NOT EXISTS (
SELECT 1
FROM conversation_user cu3
WHERE cu3.conversation_id = cu1.conversation_id
AND cu3.user_id NOT IN (3,32)
);
Run Code Online (Sandbox Code Playgroud)
user_id = 3这与其他两个查询类似,只是如果多次链接,它不会返回多行。