Для целей этого запроса было бы удобно, если бы клиент мог запросить что-то похожее на это:
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], то используйте другое недопустимое имя пользователя для разделения значений.
Если [пользователь] на самом деле является INT, вы можете CAST каждый элемент в CHAR (11) при создании AllUsers.
CAST к NVARCHAR (500) является произвольным. Без этого вы получите ошибку, что типы данных на привязке и рекурсивной части не совпадают. Вы должны рассчитать значение, превышающее 500, исходя из длины [пользователя] и максимального количества пользователей, которые могут участвовать в одном разговоре.