以高级方式选择WHERE

时间:2013-04-30 13:20:43

标签: mysql sql join

我正在尝试检查数据库是否存在特定组合。

表: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)

如果只有对话的对话,我想获得该对话的ID,否则result应该成为0

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

9 个答案:

答案 0 :(得分: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

答案 1 :(得分: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 |

这将为您提供所有会话ID及其状态

Modified

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

答案 2 :(得分: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更多的用户。

答案 3 :(得分: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

答案 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中不起作用,但很多方法都可以。我希望MySQL中的查询5和6非常有效(通过查询比组更有效)。

你的情况有所不同,你想要确切的关系划分,而那个问题/答案是关于(简单的)关系划分,所以你可以像这样写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))

答案 5 :(得分: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 */

答案 6 :(得分:2)

SELECT ID FROM CONVERSATIONS WHERE ID IN 
(SELECT CONVERSATIONS FROM CONVERSATION_USERS 
GROUP BY CONVERSATIONS HAVING COUNT(DISTINCT USER) >= 2)

答案 7 :(得分:2)

这是“set-within-sets”查询的示例。对于这些,我喜欢将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在对话中
  • 对话中没有其他用户

答案 8 :(得分:2)

如果我正确地阅读了您的要求,您需要一个任何对话的ID,其中唯一的人是(例如)1,2,3和4,并且所有这些人都在其中。如果不是,你想要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

编辑 - 上述查询的修改版本仅返回仅涉及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