2011-06-08 46 views
4

我有一个对话表和一个用户对话表。查询SQL Server中对话中用户的完全匹配

CONVERSATION 
Id, Subject, Type 

USERCONVERSATION 
Id, UserId, ConversationId 

我需要做一个基于UserIds列表的SQL查询。所以,如果我有三个UserIds用于相同的ConversationId,那么我需要执行一个查询,如果我提供了相同的三个userIds,它将返回它们完全匹配的ConversationId。

+0

你想涉及_only_三个用户的对话,或所涉及的三个用户_and任意数量的额外users_的? – 2011-06-08 07:00:37

+0

只有这三个用户。把它想象成一个电话信息。如果我与3个人开始对话,那么这3个(或n个)人会使用相同的conversationId进入UserConversation表。如果我然后开始一个全新的对话,恰好有相同的3(或n)个人,而不是创建3个新的条目,它将返回conversationId,然后新的消息可以添加到现有的对话中。 – mickyjtwin 2011-06-08 07:08:47

+0

如果你正在设计数据库,我会批评你使用'Id'作为列名的事实,而不是包含'Conversation.ConversationID'这样的表名,因为不同表中的列意味着相同的东西有不同的名字。另外,USERCONVERSATION表可能不需要Id列。这是一个多对多的连接表,这些几乎都不需要自己的ID。 – ErikE 2011-06-08 08:04:57

回答

4

假设同一个用户不能在一个UserConversation两次:

SELECT ConversationID 
FROM UserConversation 
GROUP BY ConversationID 
HAVING 
    Count(UserID) = 3 -- this isn't necessary but might improve performance 
    AND Sum(CASE WHEN UserID IN (1, 2, 3) THEN 1 ELSE 0 END) = 3 

这也适用于:

SELECT ConversationID 
FROM 
    UserConversation UC 
    LEFT JOIN (
     SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 
    ) U (UserID) ON UC.UserID = U.UserID 
GROUP BY ConversationID 
HAVING 
    Count(U.UserID) = 3 
    AND Count(UC.UserID) = 3 

如果您发现任何一个查询的性能都很差,那么采用两步法可能会有所帮助:首先查找包含至少至少所需参与方的所有会话,然后从该集合中排除包含任何其他参与方的那些会话。当然索引会有很大的不同。

摆脱UserConversation中的ID列可以通过每页获取更多行来提高性能,从而每次读取的数据量更多(大约多50%!)。如果您的Id列不仅是PK,而且还是聚簇索引,那么请立即将聚簇索引更改为ConversationId, UserId(反之亦然,这取决于最常见的用法)!

如果您需要性能方面的帮助发表评论,我会尽力帮助您。

P.S.这里的另一个大胆的想法,但它可能不执行,以及(虽然事情有时会令你感到惊讶):

SELECT 
    Coalesce(C.ConversationID, UC.ConversationID) ConversationID 
    -- Or could be Min(C.ConversationID) 
FROM 
    Conversation C 
    CROSS JOIN (
     SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 
    ) U (UserID) 
    FULL JOIN UserConversation UC 
     ON C.ConversationID = UC.ConversationID 
     AND U.UserID = UC.UserID 
GROUP BY Coalesce(C.ConversationID, UC.ConversationID) 
HAVING Count(*) = Count(U.UserID) 
+0

我会对你对提高性能的想法感兴趣 – mickyjtwin 2011-06-08 08:33:56

+0

@micky我发布了一些东西,你怎么看待它们?你可以从'UserConversation'中删除'Id'吗? “UserConversation”表上的聚簇索引和非聚簇索引是什么?你知道'ConversationID'还是'UserID'会更频繁地查询UserConversation吗?对我给你的问题进行测试,看看最好的方法。如果你发布了我所说的所有结果,我会在稍后帮助你...我现在必须走了! – ErikE 2011-06-08 08:37:37

0

我的解决办法是错误的,可惜... 我强烈建议使用的Erik's solutions一个...

问候

+0

@mickyjtwin此答案不符合您的要求。它将返回涉及三个用户*和任何其他数量的用户*的对话。 – ErikE 2011-06-08 08:00:43

+0

@Erik - 这是怎么回事?如果您将用户数放在'@ numberOfUsersIds'中,那么它只会查找指定用户参与的用户数量。 – 2011-06-08 08:06:26

+1

由于您的WHERE子句排除了其他用户,因此Count将最大限度地列于列表中的用户,而不计算其他用户被排除在外。尝试这个查询,并且在它不应该返回时返回一个行,因为UserID 4是对话的一部分:'SELECT ConversationID FROM(SELECT 1,1 UNION ALL SELECT 1,2,UNION ALL SELECT 1,3,UNION ALL SELECT 1,4 )UserConversation(ConversationID,UserID)WHERE UserID IN(1,2,3)GROUP BY ConversationID HAVING Count(UserID)= 3' – ErikE 2011-06-08 08:10:39