Группировка T-SQL по любому из двух столбцов - PullRequest
0 голосов
/ 24 октября 2019

У меня есть таблица, в которой я храню историю чата следующим образом:

Id       From        To        Text         Hour
=================================================
1         A          B         Msg_A_B1     00:01
2         A          B         Msg_A_B2     00:02
3         B          A         Msg_B_A1     00:03
4         A          B         Msg_A_B3     00:05
5         C          A         Msg_C_A1     00:11
6         A          C         Msg_A_C1     00:12
7         C          A         Msg_C_A2     00:14
8         D          B         Msg_D_B1     00:17

Я хочу создать список заголовков чата из данных для конкретного пользователя. Правила:

  • Я хочу получить начальный (первый) "час" каждого чата
  • и последнее сообщение чата для конкретного пользователя
  • , заказанного«Час» по возрастанию

Например, если пользователь «А», я хочу получить

Correspondant       Text        Hour
=======================================
B                  Msg_A_B3     00:01
C                  Msg_C_A2     00:11

Или для пользователя «Б»:

Correspondant       Text        Hour
=======================================
A                  Msg_A_B3     00:01
D                  Msg_D_B1     00:17

Я могу сделать это, используя временные таблицы, но я ищу более простое и быстрое решение.

Эта информация может привести к использованию хранимых процедур, но также допускается правильное использование представлений.

Ответы [ 2 ]

1 голос
/ 24 октября 2019

Вам не хватает столбца группировки, чтобы пометить чат между A и B как "принадлежащий друг другу" , не глядя, является ли A или B From или To.

Это ваш дизайн стола, который делает вещи сложными. Ниже моего предложения я добавлю несколько советов, как это можно сделать лучше:

Ваш макет для имитации вашей проблемы:

DECLARE @mockupTable TABLE(Id INT,[From] VARCHAR(100),[To] VARCHAR(100),[Text] VARCHAR(100),[Hour] TIME(0))

INSERT INTO @mockupTable VALUES
 (1,'A','B','Msg_A_B1','00:01')
,(2,'A','B','Msg_A_B2','00:02')
,(3,'B','A','Msg_B_A1','00:03')
,(4,'A','B','Msg_A_B3','00:05')
,(5,'C','A','Msg_C_A1','00:11')
,(6,'A','C','Msg_A_C1','00:12')
,(7,'C','A','Msg_C_A2','00:14')
,(8,'D','B','Msg_D_B1','00:17');

- Запрос

WITH cte AS
(
    SELECT t.*
          ,CONCAT(CASE WHEN t.[From]>t.[To] THEN t.[To] ELSE t.[From] END,'-',CASE WHEN t.[From]>t.[To] THEN t.[From] ELSE t.[To] END) AS ChatID
    FROM @mockupTable t
)
,FindFirstAndLast AS
(
    SELECT cte1.ChatID
          ,(SELECT TOP 1 Id FROM cte cte2 WHERE cte2.ChatID=cte1.ChatID ORDER BY cte2.[Hour] ASC) AS FirstId
          ,(SELECT TOP 1 Id FROM cte cte2 WHERE cte2.ChatID=cte1.ChatID ORDER BY cte2.[Hour] DESC) AS LastId
    FROM cte cte1
    GROUP BY cte1.ChatID
)
SELECT fal.ChatID
      ,tFirst.[From] AS FirstFrom 
      ,tFirst.[To] AS FirstTo 
      ,tFirst.[Hour] AS FirstHour 
      ,tLast.[From] AS LastFrom 
      ,tLast.[To] AS LastTo 
      ,tLast.[Text] AS LastText
FROM FindFirstAndLast fal
INNER JOIN @mockupTable tFirst ON fal.FirstId=tFirst.Id
INNER JOIN @mockupTable tLast ON fal.LastId=tLast.Id;

Идея вкратце:

  • Первый CTE создаст ChatID путем объединения From и To в отсортированном виде. При этом сообщение от A до B получит тот же ChatID, что и сообщение от B до A.
  • Второй CTE будет использовать коррелированный подзапрос, чтобы найти первый и последний идентификатор сообщения, сгруппированные дляранее вычисленный ChatID.
  • В последнем SELECT эти идентификаторы сообщений будут использоваться для объединения соответствующих строк.

В результате вы получите все, что вам нужно. Это на вас, чтобы поставить его в необходимом формате:

+--------+-----------+---------+-----------+----------+--------+----------+
| ChatID | FirstFrom | FirstTo | FirstHour | LastFrom | LastTo | LastText |
+--------+-----------+---------+-----------+----------+--------+----------+
| A-B    | A         | B       | 00:01:00  | A        | B      | Msg_A_B3 |
+--------+-----------+---------+-----------+----------+--------+----------+
| A-C    | C         | A       | 00:11:00  | C        | A      | Msg_C_A2 |
+--------+-----------+---------+-----------+----------+--------+----------+
| B-D    | D         | B       | 00:17:00  | D        | B      | Msg_D_B1 |
+--------+-----------+---------+-----------+----------+--------+----------+

Некоторые идеи о дизайне

Я бы использовал

  • один стол Person дляваши собеседники.
  • вторая таблица Chat для чата с ChatID.
  • one m:n таблица сопоставления ChattingPerson с JoinTime, ChatID и PersonID, оба в виде FK,Здесь вы можете установить временные метки, такие как LastAction или пометить статус (активный, ушел, ...)
  • еще одну таблицу Message для сообщений со временем, текстом и ChatPersonID как FK.

Ваши преимущества

  • Открывающий может явно пригласить больше людей (или ограничить его одним для чата person2person ) или просто подождать участников.
  • При запуске чата создается строка в таблице чата, первая строка в таблице ChattingPerson для отметки открывателя и, в конечном итоге, первая строка сообщения.
  • После следующих сообщений добавьте - если еще не существует - строку к ChatPerson (с новым участником) и строку сообщения.
  • Идентификатор таблицы ChatPerson даст вамChatID и PersonID.

  • Вы можете фильтровать по чату и / или по человеку.

  • Со временем между A и B могут быть отдельные чаты
  • Вы можете контролировать тип чата с помощью PersonCount-Constraint
  • Вы можете принудить, что новый ChatPerson может быть добавлен только с помощью открывателя
  • Вы можете создать определенный чаттипы (например, "person2person" ) с шаблоном

Happy Coding: -)

0 голосов
/ 24 октября 2019

Давайте сделаем это Создание представления:

Сначала давайте загрузим данные в таблицу t1:

create table t1 (Id int,[From] varchar(10),[To] varchar(10),Text varchar(100),Hour time(0))

insert into t1 values (1,'A','B','Msg_A_B1','00:01')
insert into t1 values (2,'A','B','Msg_A_B2','00:02')
insert into t1 values (3,'B','A','Msg_B_A1','00:03')
insert into t1 values (4,'A','B','Msg_A_B3','00:05')
insert into t1 values (5,'C','A','Msg_C_A1','00:11')
insert into t1 values (6,'A','C','Msg_A_C1','00:12')
insert into t1 values (7,'C','A','Msg_C_A2','00:14')
insert into t1 values (8,'D','B','Msg_D_B1','00:17')

Затем давайте создадим представление

create view vChats
as 
with cte as (
select  left([text],len([text])-1) as chat,
        t.*
from t1 as t
),
cte2 as (
select chat,
        min(hour) as minHour,
        max(text) as maxText
from cte
group by chat
),
cte3 as (select distinct [From] as [User] 
            from t1 
         UNION 
         select distinct [To] as [User] 
            from t1
)
select c3.[User],
        t.[To] as Correspondant,
        c.maxText as [Text],
        c.minHour as [Hour]
from cte2 as c
    inner join t1 as t ON c.maxText = t.[Text]
    inner join cte3 as c3 ON c3.[User] = t.[From]
UNION
select c3.[User],
        t.[From] as Correspondant,
        c.maxText as [Text],
        c.minHour as [Hour]
from cte2 as c
    inner join t1 as t ON c.maxText = t.[Text]
    inner join cte3 as c3 ON c3.[User] = t.[To]

После этогоВы можете использовать для получения всех сообщений для каждого пользователя, как это:

select *
from vChats
where [User] = 'A'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...