2013-04-30 53 views
5

我想检查数据库是否存在特定的组合。选择高级方式

表:conversations

+----+ 
| id | 
+----+ 
| 1 | 
| 2 | 
| 3 | 
| 4 | 
| 5 | 
+----+ 

表:conversations_users

+----+--------------+------+ 
| id | conversation | user | 
+----+--------------+------+ 
| 1 | 1   | 1 | 
| 2 | 1   | 2 | 
| 3 | 2   | 1 | 
| 4 | 2   | 2 | 
| 5 | 2   | 3 | 
| 6 | 2   | 4 | 
| 7 | 3   | 2 | 
| 8 | 3   | 3 | 
| 9 | 4   | 2 | 
| 10 | 4   | 4 | 
+----+--------------+------+ 

然后我想进行查询,以获得其中这些用户在同一个对话的对话:

Users: 1,2,3,4 (Only them, no else) 

如果只有那些人在进行对话,我想得到第在谈话,否则result应该成为0

任何人有任何想法如何做到这一招?

+1

这将是我们更容易帮助你,如果你设置了[SQL小提琴(http://sqlfiddle.com/) – 2013-04-30 13:22:16

+1

由于用户5的存在,我认为你不要求所有四个用户(1,2,3,4)都在您正在寻找的对话中,但是您正在寻找包含(1,2,3,4)用户的任何组合的任何对话。你能澄清这一点吗? – gillyspy 2013-04-30 14:27:42

回答

2

这是一个“设置中集”查询的一个例子。对于这些,我喜欢使用group byhaving子句:

select conversation 
from conversation_users cu 
group by conversation 
having SUM(user = 1) > 0 and 
     sum(user = 2) > 0 and 
     sum(user = 3) > 0 and 
     sum(user = 4) > 0 and 
     sum(user not in (1, 2, 3, 4)) = 0 

having子句中的每一个条件对应于在问题中指定的五个条件之一:

  • 用户1处于谈话
  • 用户2处于谈话
  • 用户3在对话
  • 用户4处于谈话
  • 没有其他用户是
+0

工程就像一个魅力。非常感谢。 – 2013-05-08 13:57:26

3

这个想法是计算给定对话中不同的用户。如果你IN条款中设定的用户数相匹配,那么你肯定只有你搜索的:

SELECT id 
FROM conversations_users 
WHERE user in (1, 2, 3, 4) 
GROUP BY id 
HAVING COUNT(DISTINCT user) = 4 

注意,这不输出对话,其中只有4个用户3出现。如果你需要这些交谈过,则:

SELECT id 
FROM conversations_users 
WHERE user in (1, 2, 3, 4) 
GROUP BY id 
HAVING COUNT(DISTINCT user) <= 4 
+0

对不起,延迟回复。我生病了,所以没有工作。那么,这工作非常好,但唯一的问题是,如果我有以下内容:http://sqlfiddle.com/#!2/859360/1问题是,它也选择有更多用户的对话,所以不只有选中的。如果你明白的话。 – 2013-05-08 08:13:09

2
SELECT 
    cs.conversation, 
    IF(csl.total = 4,'yes','no') AS AllIn 
FROM conversations_users AS cs 
    LEFT JOIN (
       SELECT 
        conversation , 
        COUNT(DISTINCT user) AS total 
       FROM conversations_users 
       WHERE user IN (1,2,3,4) 
       GROUP BY conversation 
      ) AS csl 
    ON csl.conversation = cs.conversation 
GROUP BY cs.conversation 

SQL Fiddle Demo

输出

| CONVERSATION | ALLIN | 
------------------------ 
|   1 | no | 
|   2 | yes | 
|   3 | no | 
|   4 | no | 

这会给你的所有谈话IDS及其状态

Modified

| CONVERSATION | ALLIN | 
------------------------ 
|   1 |  0 | 
|   2 |  2 | 
|   3 |  0 | 
|   4 |  0 | 
+0

非常感谢。这对我有效。 – 2013-05-08 11:39:11

+0

虽然是一个很好的解决方案,但我的理解是,你想排除那些除了指定的4之外的人的对话?还是我错误地读了你原来的问题? – Kickstart 2013-05-08 11:55:28

2

我认为这是你在找什么:

SELECT cu.conversation 
FROM (select conversation, count(distinct user) usercnt 
     from conversations_users 
    group by conversation) t 
    JOIN conversations_users cu on t.conversation = cu.conversation 
WHERE cu.user in (1, 2, 3, 4) AND t.usercnt = 4 
GROUP BY cu.conversation 
HAVING COUNT(DISTINCT cu.user) = 4 

SQL Fiddle Demo

这使用子查询来确定与每个会话相关的用户的总数。这需要确保您的对话中没有更多的用户,而不仅仅是1,2,3和4。

2

如果我正确理解你的问题,你可以这样做:

SELECT 
    conversation 
FROM 
    conversations_users 
GROUP BY 
    conversation 
HAVING 
    COUNT(
    DISTINCT CASE WHEN user IN (1,2,3,4) THEN user END 
)=4 AND 
    COUNT(DISTINCT user)=4 
2

假如你有一个users表,以及:

SELECT id 
FROM conversations AS c 
WHERE NOT EXISTS 
     (SELECT * 
     FROM users AS u 
     WHERE u.id IN (1, 2, 3, 4) 
      AND NOT EXISTS 
       (SELECT * 
       FROM conversations_users AS cu 
       WHERE cu.user = u.id 
        AND cu.conversation = c.id 
      ) 
    ) 
    AND NOT EXISTS 
     (SELECT * 
     FROM conversations_users AS co  -- and only them 
     WHERE co.conversation = c.id 
      AND co.user NOT IN (1, 2, 3, 4) 
    ) ; 

如果没有users表或者你不喜欢使用它(可以理解,但无论如何),您可以替换此部分:

WHERE NOT EXISTS 
     (SELECT * 
     FROM users AS u 
     WHERE u.id IN (1, 2, 3, 4) 
      AND NOT EXISTS 

用:

WHERE NOT EXISTS 
     (SELECT * 
     FROM (SELECT 1 AS id UNION SELECT 2 UNION 
       SELECT 3  UNION SELECT 4) AS u 
     WHERE NOT EXISTS 

上面的查询,而被一般不会出现在MySQL非常有效的(怪双嵌套和幼稚优化器)。 GROUP BY/COUNT方式可能更高效 - 但请使用您的数据进行测试。你也可以找到更多的方法(超过10)来回答这种问题,在这个答案:How to filter SQL results in a has-many-through relation其中一些不在MySQL中工作,但很多都做。我期望查询5和6在MySQL中非常高效(比查询组更有效)。

你的情况有区别,你想确切的关系划分,而这个问题/答案是约(简单)的关系划分,所以你可以写5是这样的:

SELECT id 
FROM conversations AS c 
WHERE EXISTS (SELECT * FROM conversations_users AS cu 
       WHERE cu.conversation = c.id AND cu.user = 1) 
AND EXISTS (SELECT * FROM conversations_users AS cu 
       WHERE cu.conversation = c.id AND cu.user = 2) 
AND EXISTS (SELECT * FROM conversations_users AS cu 
       WHERE cu.conversation = c.id AND cu.user = 3) 
AND EXISTS (SELECT * FROM conversations_users AS cu 
       WHERE cu.conversation = c.id AND cu.user = 4) 
AND NOT EXISTS (SELECT * FROM conversations_users AS cu 
        WHERE cu.conversation = c.id AND cu.user NOT IN (1,2,3,4)) 
+0

+1关系分裂1.0。下一步:查找(并枚举)共享相同会话集的用户的等价组; - ) – wildplasser 2013-04-30 13:52:34

+0

非常感谢,但“用户”表对此不是必需的。由于用户ID位于'converstations_users'表中。 – 2013-05-08 08:15:31

+0

我没有说这是需要的。但是如果你有一个用户表,查询更容易编写。 – 2013-05-08 08:22:38

2

请查询并加入简单易于阅读。

由于用户5的存在,我假设你不需要所有4个用户(1,2,3,4)在你正在寻找的对话中,而是只有任何对话包括这4个用户的任何组合。

DEMO

select distinct 
    cu.conversation 
from 
    conversations_users cu 
     left join 
    conversations_users cu2 ON cu.conversation = cu2.conversation 
where 
    cu.user in (1 , 2, 3, 4) 
     and cu2.user in (1 , 2, 3, 4) 
     and cu.user != cu2.user /* include this clause if you need to exclude conversations of a user with themselves */ 

让我知道,如果你想要,而不是只在那里所有4个用户参与了对话。

你想删除其他会话吗?当你说“结果应该变为0”时,你指的是行数或对话ID的值?如果是后者,然后使用:

select distinct 
    case 
     when 
      cu.user in (1 , 2, 3, 4) 
       and cu2.user in (1 , 2, 3, 4) 
     then 
      cu.conversation 
     else 0 
    end conversation 
from 
    conversations_users cu 
     left join 
    conversations_users cu2 ON cu.conversation = cu2.conversation 
where 
    1 = 1 
      and cu.user != cu2.user /* include this clause if you need to exclude conversations of a user with themselves */ 
2
SELECT ID FROM CONVERSATIONS WHERE ID IN 
(SELECT CONVERSATIONS FROM CONVERSATION_USERS 
GROUP BY CONVERSATIONS HAVING COUNT(DISTINCT USER) >= 2) 
+1

让我知道这是否适合您。欢呼声。 – Androidz 2013-04-30 14:29:14

+0

谢谢。但是这不包括任何地方用户的ID。因此,它可能只选择有2个用户的所有对话? – 2013-05-08 08:14:33

2

如果我正确地读出你的要求,你想在那里的唯一的人(例如)1,2,3和4的任何谈话的ID中的谈话,所有这些人都在其中。如果不是,您希望0返回该对话。

如果是这样那么这样的事情

SELECT CASE WHEN MatchCount = 4 AND UnMatchCount IS NULL THEN conversations.id ELSE 0 END 
FROM conversations 
LEFT OUTER JOIN (SELECT conversation, COUNT(DISTINCT user) AS MatchCount FROM conversations_users WHERE user IN (1,2,3,4) GROUP BY conversation) Sub1 ON conversations.id = Sub1.conversation 
LEFT OUTER JOIN (SELECT conversation, COUNT(DISTINCT user) AS UnMatchCount FROM conversations_users WHERE user NOT IN (1,2,3,4) GROUP BY conversation) Sub2 ON conversations.id = Sub2.conversation 

EDIT - 修改了上面的查询的版本,只带回那里只有那些4个用户参与对话的ID。玩这个似乎是一个非常有效的方法。

SELECT conversations.id 
FROM conversations 
LEFT OUTER JOIN (SELECT conversation, COUNT(DISTINCT user) AS MatchCount FROM conversations_users WHERE user IN (1,2,3,4) GROUP BY conversation) Sub1 ON conversations.id = Sub1.conversation 
LEFT OUTER JOIN (SELECT conversation, COUNT(DISTINCT user) AS UnMatchCount FROM conversations_users WHERE user NOT IN (1,2,3,4) GROUP BY conversation) Sub2 ON conversations.id = Sub2.conversation 
WHERE MatchCount = 4 
AND UnMatchCount IS NULL 
+0

也许我做错了什么,但是当我使用它并打印输出时,我只会得到以下内容:'[[当CASE当MatchCount = 4并且UnMatchCount为NULL THEN conversations.id ELSE 0 END] => 0' – 2013-05-08 08:17:53

+1

Just double检查它,它确实工作。如果在谈话中只有4个人,那么它会发出id,否则为零。如果需要,您可以轻松地为返回的列提供别名。 – Kickstart 2013-05-08 09:38:01