我有一个表格,列出了同一个对话中的用户。例如:
id | conversation | user
1 | 1 | Bob
2 | 1 | Jane
3 | 2 | Tim
4 | 2 | Lily
5 | 1 | Rick
6 | 3 | Lily
7 | 1 | Tim
我现在想要检查用户何时想要与其他用户开始对话,无论他们之前是否曾与其他用户进行对话(仅限于此)。
EG。蒂姆希望与莉莉开始新的对话。在他们成为对话中唯一的用户之前,他们是否已经进行了对话? sql查询将确定他们作为独占参与者在对话2中。
编辑: 这是我尝试实现返回参与者专门交谈的conversation_id所需的结果。 有趣的是,此尝试确实会返回所需的结果:
SELECT in2.[conversation] AS cid, COUNT(in2.[conversation]) AS [matchedParticipants], (SELECT COUNT(in1.[conversation]) FROM [cInboxMembers] in1 WHERE in1.[conversation] = in2.[conversation] GROUP BY in1.[conversation]) AS totalParticipants FROM [cInboxMembers] in2 WHERE [username] IN ('Tim','Lily') GROUP BY in2.[conversation] HAVING COUNT(in2.[conversation]) = '2' AND (SELECT COUNT(in1.[conversation]) FROM [cInboxMembers] in1 WHERE in1.[conversation] = in2.[conversation] GROUP BY in1.[conversation]) = '2'
在这次尝试中,我列出了我想要找到的两个用户,然后列出了应该在独占对话中的参与者数量(即'2'),并将结果限制在[matchedParticipants] = 2和totalParticipants = 2。
看起来非常笨重的部分是HAVING部分,因为无法引用列别名的限制。
答案 0 :(得分:2)
好的,我有点想到这个想法;我相信它会起作用,但你应该仔细检查。基本上它的作用是为用户添加每个对话的行号,并使用表值来存储传入的用户ID。然后,它选择行号等于用户数的任何会话,并且等于会话中的人数。双子查询可能是不理想的,也许sql wiz可以进一步优化它。
CREATE TABLE #ConversationPeople
(
ID int NOT NULL IDENTITY(1,1) PRIMARY KEY
,[conversation] int NOT NULL
,[user] int NOT NULL
)
INSERT INTO #ConversationPeople
([conversation], [user])
VALUES
(1,1)
,(1,2)
,(2,3)
,(2,4)
,(2,1)
,(1,5)
,(3,4)
,(1,3)
GO
CREATE TYPE dbo.UserList AS TABLE
([user] int)
GO
DECLARE @users dbo.UserList
INSERT INTO @users VALUES (3),(4)
SELECT CP_Data.*, CP.ROW
FROM (
SELECT CP.ID, ROW_NUMBER() OVER(PARTITION BY CP.[conversation] ORDER BY CP.[user]) AS [ROW]
FROM
@users U
JOIN #ConversationPeople CP
ON CP.[user] = U.[user]) CP
JOIN #ConversationPeople CP_Data
ON CP.ID = CP_Data.ID
WHERE CP.ROW = (SELECT COUNT(*) FROM @users)
AND CP.ROW = (SELECT COUNT(*) FROM #ConversationPeople WHERE [conversation] = CP_Data.conversation)
DROP TYPE dbo.UserList
DROP TABLE #ConversationPeople
GO
答案 1 :(得分:1)
出于此查询的目的,如果客户端可以查询如下所示的内容将会很方便:
conversation AllUsers
1 Bob Jane Rick Tim
2 Lily Tim
(会话3只有一个用户,我认为这个用户无效,可以忽略。)
如何根据问题的表结构来实现这一点?本质上,我想在任意数量的列上进行PIVOT,然后连接它们的值,以按字母顺序创建对话中所有参与者的空格分隔列表。不幸的是,PIVOT要求您列出要转换为列的每个值。递归CTE救援:
CREATE TABLE Conversations (
id INT NOT NULL,
conversation INT NOT NULL,
[user] NVARCHAR(50) NOT NULL
)
INSERT INTO Conversations (id, conversation, [user])
SELECT 1, 1, 'Bob'
UNION SELECT 2, 1, 'Jane'
UNION SELECT 3, 2, 'Tim'
UNION SELECT 4, 2, 'Lily'
UNION SELECT 5, 1, 'Rick'
UNION SELECT 6, 3, 'Lily'
UNION SELECT 7, 1, 'Tim'
;WITH ConversationNext AS (
SELECT C1.conversation, C1.[user], MIN(C2.[user]) AS NextUser
FROM Conversations C1
JOIN Conversations C2
ON C2.conversation = C1.conversation
AND C2.[user] > C1.[user]
GROUP BY C1.conversation, C1.[user]
),
ConversationRoot AS (
SELECT conversation, MIN([user]) AS [user], MIN(NextUser) AS NextUser,
CAST(MIN([user]) + ' ' + MIN(NextUser) AS NVARCHAR(500)) AS AllUsers,
2 AS NumberOfParticipants
FROM ConversationNext
GROUP BY conversation
),
ConversationRecursive AS (
SELECT *
FROM ConversationRoot
UNION ALL
SELECT ConversationRecursive.conversation, ConversationRecursive.[user], ConversationNext.NextUser,
CAST(ConversationRecursive.AllUsers + ' ' + ConversationNext.NextUser AS NVARCHAR(500)),
ConversationRecursive.NumberOfParticipants + 1
FROM ConversationRecursive
JOIN ConversationNext
ON ConversationNext.conversation = ConversationRecursive.conversation
AND ConversationNext.[user] = ConversationRecursive.NextUser
),
Final AS (
SELECT Conversation, MAX(NumberOfParticipants) as N
FROM ConversationRecursive
GROUP BY Conversation
)
SELECT ConversationRecursive.conversation, ConversationRecursive.AllUsers
FROM Final
JOIN ConversationRecursive
ON ConversationRecursive.conversation = Final.conversation
AND ConversationRecursive.NumberOfParticipants = Final.N
DROP TABLE Conversations
起初我虽然这应该在一个存储过程中完成,该存储过程为用户列表采用表值参数,但现在我认为在客户端代码中以字母顺序构造以空格分隔的用户列表可能更容易订单,而不是使用表值参数。
如果空格是[user]中的有效字符,则使用用户名中无效的其他内容来分隔值。
如果[user]实际上是INT,则可以在构造AllUsers时将每个元素CAST为CHAR(11)。
CAST到NVARCHAR(500)有点武断。如果没有它,您将收到锚点和递归部分上的数据类型不匹配的错误。根据[用户]的长度和单个对话中可以使用的最大用户数,您应该计算出比500更好的值。
答案 2 :(得分:1)
这不是很有效,但假设你有一个像你所示的表:
id | conversation | user
1 | 1 | Bob
2 | 1 | Jane
3 | 2 | Tim
4 | 2 | Lily
5 | 1 | Rick
6 | 3 | Lily
7 | 1 | Tim
你可以在每个对话中获得一些参与者到临时表
SELECT
T1.Conversation,
COUNT(*) NumberOfUsers
INTO
#TEMP
FROM
YourTable T1
INNER JOIN YourTable T2
ON T1.Conversation = T2.Conversation
AND T1.id <> T2.id
AND T1.username = 'Tim'
GROUP BY T1.Conversation
然后使用参与者数量= 1和名称=莉莉
再次过滤它SELECT
*
FROM
YourTable T
INNER JOIN #TEMP T2
ON T.Conversation = T2.Conversation
AND NumberOfUsers = 1
AND T.UserName = 'lily'
-- AND T.UserName = 'jane'
如果你得到一排他有一个&#34;私人&#34;和她谈话,如果你得到0排,他没有。 有了这个,你可以过滤你想要的数字,如NumberOfUsers&gt; 50,如果你想进行非常公开的对话......
如果您不想使用临时表,甚至可以使用第一个选择作为子查询。
如果您的表变得很大,这种方法可能会产生性能问题,但您可以使用索引和其他方法对其进行优化