SELECT WHERE ON 高级方式
Posted
技术标签:
【中文标题】SELECT WHERE ON 高级方式【英文标题】:SELECT WHERE ON an advanced way 【发布时间】:2013-04-30 13:20:42 【问题描述】:我正在尝试检查数据库是否存在特定组合。
表: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
有人知道如何做这个技巧吗?
【问题讨论】:
如果您设置SQL Fiddle,我们会更容易为您提供帮助 由于用户 5 的存在,我假设您不需要所有四个用户 (1,2,3,4) 都在您正在寻找的对话中,而是您正在寻找包含 (1,2,3,4) 用户的任意组合的任何对话。你能澄清一下吗? 【参考方案1】:这个想法是计算给定对话中的不同用户。如果它与您在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
【讨论】:
抱歉回复晚了。我生病了,所以没有上班。好吧,这很好用,但唯一的问题是,如果我有以下内容:sqlfiddle.com/#!2/859360/1 问题是它还会选择有更多用户的对话,所以不仅仅是选择的。如果你明白的话。【参考方案2】:这是“set-within-sets”查询的示例。对于这些,我喜欢将group by
与having
子句一起使用:
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 正在对话中 对话中没有其他用户【讨论】:
像魅力一样工作。非常感谢。【参考方案3】:我想这就是你要找的东西:
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 个。
【讨论】:
【参考方案4】: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 |
【讨论】:
虽然是一个很好的解决方案,但我的理解是你想排除除了你指定的 4 人以外的人的对话?还是我看错了你原来的问题?【参考方案5】:SELECT ID FROM CONVERSATIONS WHERE ID IN
(SELECT CONVERSATIONS FROM CONVERSATION_USERS
GROUP BY CONVERSATIONS HAVING COUNT(DISTINCT USER) >= 2)
【讨论】:
谢谢。但这不包括任何地方的用户的 ID。所以它可能只选择有 2 个用户的所有对话?【参考方案6】:保持查询和连接简单易读。
由于用户 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 */
【讨论】:
【参考方案7】:如果我正确理解你的问题,你可以使用这个:
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
【讨论】:
【参考方案8】:假设您也有一个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 在 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))
【讨论】:
+1 关系除法 1.0。下一步:查找(并枚举)共享同一组对话的用户等价组;-) 非常感谢,但此操作不需要users
表。因为用户 ID 在 converstations_users
表中。
我没有说它是必需的。但是如果你确实有一个 users 表,查询会更容易编写。【参考方案9】:
如果我正确阅读了您的要求,您需要任何对话的 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
【讨论】:
也许我做错了什么,但是当我使用它并打印输出时,我得到以下信息:[CASE WHEN MatchCount = 4 AND UnMatchCount IS NULL THEN conversations.id ELSE 0 END] => 0
仔细检查一下,它确实有效。如果对话中只有这 4 个人,它会输出 id,否则为零。如果需要,您可以轻松地为返回的列指定别名。以上是关于SELECT WHERE ON 高级方式的主要内容,如果未能解决你的问题,请参考以下文章