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)

如果有一个只有那些在的对话,我想得到那个对话的 id,否则result应该变成0

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

4

9 回答 9

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
于 2013-04-30T13:22:18.233 回答
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 小提琴演示

输出

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

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

修改的

| CONVERSATION | ALLIN |
------------------------
|            1 |     0 |
|            2 |     2 |
|            3 |     0 |
|            4 |     0 |
于 2013-04-30T13:32:14.480 回答
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 小提琴演示

这使用子查询来确定与每个对话关联的用户总数。这是为了确保对话中的用户数不超过 1、2、3 和 4。

于 2013-04-30T13:33:14.280 回答
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
于 2013-04-30T13:43:04.660 回答
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 种)来回答这类问题,在这个答案中:如何过滤 SQL 导致具有多通关系的结果其中一些在 MySQL 中不起作用,但很多在 MySQL 中起作用。我希望查询 5 和 6 在 MySQL 中非常有效(比 group by 查询更有效的级别)。

您的情况有所不同,您需要精确的关系划分,而问题/答案是关于(简单)关系划分,所以您可以这样写 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))
于 2013-04-30T13:45:53.380 回答
2

保持查询和连接简单易读。

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

演示

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 */
于 2013-04-30T14:16:30.630 回答
2
SELECT ID FROM CONVERSATIONS WHERE ID IN 
(SELECT CONVERSATIONS FROM CONVERSATION_USERS 
GROUP BY CONVERSATIONS HAVING COUNT(DISTINCT USER) >= 2)
于 2013-04-30T14:18:28.387 回答
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

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

  • 用户 1 正在对话中
  • 用户 2 正在对话中
  • 用户 3 正在对话中
  • 用户 4 正在对话中
  • 对话中没有其他用户
于 2013-04-30T14:30:48.857 回答
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
于 2013-04-30T14:53:28.983 回答