我有一个表格,其中包含参与对话的用户关系,如下所示:
CREATE TABLE `so` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`user_id` int(11) NOT NULL,
`conversation_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `so`
ADD UNIQUE KEY `uc` (`user_id`,`conversation_id`) USING BTREE;
INSERT INTO `so` (`id`, `user_id`, `conversation_id`) VALUES
(1, 1, 1),
(3, 1, 2),
(2, 2, 1),
(4, 2, 2),
(5, 3, 2);
根据样本数据,用户1和2的ID为1,用户为1,2,3,ID为2的会话。
我需要为用户ID列表获取唯一conversation_id
。
我目前的查询是:
SELECT conversation_id, COUNT(user_id) as usersCount
FROM so
WHERE user_id IN (1,2)
GROUP BY conversation_id
HAVING usersCount = 2
ORDER BY NULL
但它为两个对话返回2行,我希望对话框的行为1。
如何选择完全属于用户1和2的行,而不是1,2,3?感谢。
更新: 出于性能原因,我无法在连接上使用子查询,因为查询中的用户列表最多可能有30个ID,而我恐怕30个子查询不是这种情况。
答案 0 :(得分:3)
您可以使用group_concat
select conversation_id
from so
group by conversation_id
having group_concat(user_id order by user_id) = '1,2';
要避免完整索引扫描,可以将原始查询放在子查询中:
SELECT a.conversation_id
FROM (
SELECT conversation_id
FROM so
WHERE user_id IN (1,2)
GROUP BY conversation_id
HAVING COUNT(conversation_id) = 2) a
JOIN so b ON a.conversation_id = b.conversation_id
GROUP BY a.conversation_id
HAVING COUNT(a.conversation_id) = 2;
答案 1 :(得分:2)
不是检查user_id
子句中的WHERE
,而是将满足该条件的行数与每个会话的总行数进行比较。
SELECT conversation_id, COUNT(*) AS allCount, SUM(user_id IN (1, 2)) AS userCount
FROM so
GROUP BY conversation_id
HAVING allCount = 2 AND allCount = userCount
答案 2 :(得分:1)
这个答案是已经给出的替代方案,并且通过不使用子选择将提供更高的效率。
HAVING COUNT(user_id IN ('1','2') OR NULL) > 0
指定您希望与用户ID 1和2进行对话。
COUNT(user_id) = 2
表示对话中只能有2个用户。
如果您没有在练习中使用它,您甚至可以从结果集中删除COUNT(user_id) as usersCount
。
SELECT conversation_id, COUNT(user_id) as usersCount
FROM so
GROUP BY conversation_id
HAVING COUNT(user_id IN ('1','2') OR NULL) > 0 AND
COUNT(user_id) = 2;
为避免完整索引扫描,您必须使用where
子句,因为@Fabricator已在其答案中显示。将条件应用于行组时,必须先将它们分组,然后执行聚合和条件,where
子句仅将条件应用于单行。你的桌子有多大兴趣?