Объединение групп последовательных строк в T-SQL и суммирование значений из каждой группы - PullRequest
2 голосов
/ 19 сентября 2019

У меня есть таблица транзакций со схемой:

CREATE TABLE Transactions (Id int IDENTITY, SessionId int, TransactionType varchar(50), DateTimeEnd datetime, DateStart datetime, Rank int);

Вот несколько примеров строк:

INSERT INTO Transactions (Id, SessionId, TransactionType, DateTimeEnd, DateStart, Rank)
VALUES
 (1, 1, 'Deposit',    '2017-01-20T11:16:33Z', '2017-01-20T11:16:33Z', 600),
 (2, 1, 'Withdrawal', '2017-01-21T11:16:33Z', '2017-01-20T11:16:33Z', 100),
 (3, 2, 'Deposit',    '2017-02-23T11:16:33Z', '2017-02-23T11:16:33Z', 500),
 (4, 1, 'Withdrawal', '2017-01-24T11:16:33Z', '2017-01-21T11:16:33Z', 150),
 (5, 1, 'Withdrawal', '2017-01-26T11:16:33Z', '2017-01-24T11:16:33Z', 150),
 (6, 2, 'Withdrawal', '2017-02-27T11:16:33Z', '2017-02-23T11:16:33Z', 200),
 (7, 1, 'Withdrawal', '2017-01-28T11:16:33Z', '2017-01-26T11:16:33Z', 10),
 (8, 1, 'Withdrawal', '2017-01-30T11:16:33Z', '2017-01-28T11:16:33Z', 10),
 (9, 1, 'Withdrawal', '2017-01-31T11:16:33Z', '2017-01-30T11:16:33Z', 10);

Мне нужен запрос T-SQL для объединения групп последовательныхстроки по SessionId, TransactionType и из каждой группы, чтобы сохранить только строку с минимальным DateTimeEnd.Кроме того, значение ранга сохраненной строки должно быть суммой значений ранга из строк группы.Запрос T-SQL должен выполняться в MS SQL Server в хранилище данных Microsoft Azure SQL.

Желаемый результат:

|    Id    |     SessionId    | Transaction |       DateTimeEnd  |      DateStart     |   Rank  |
|----------|------------------|-------------|--------------------|--------------------|---------|
|    1     |         1        |      Deposit|2017-01-20T11:16:33Z|2017-01-20T11:16:33Z|   600   |
|    2     |         1        |   Withdrawal|2017-01-21T11:16:33Z|2017-01-20T11:16:33Z|   100   |
|  4       |         1        |   Withdrawal|2017-01-24T11:16:33Z|2017-01-21T11:16:33Z|   300   |
|  7       |         1        |   Withdrawal|2017-01-28T11:16:33Z|2017-01-26T11:16:33Z|    30   |
|    3     |         2        |      Deposit|2017-02-23T11:16:33Z|2017-02-23T11:16:33Z|   500   |
|    6     |         2        |   Withdrawal|2017-02-27T11:16:33Z|2017-02-23T11:16:33Z|   200   |

Я испробовал множество подходов и не смог его реализовать.

Ответы [ 2 ]

2 голосов
/ 19 сентября 2019

Как указывает GMB, это проблема пробелов и островков.Поскольку вы хотите сохранить первую строку, я собираюсь предложить подход lag() вместо разницы номеров строк:

SELECT SessionId, TransactionType, DateTimeEnd,DateStart, sumRank
FROM (SELECT t.*,
             SUM(Rank) OVER (PARTITION BY SessionId, TransactionType, grp) as sumRank
      FROM (SELECT t.*,
                   SUM(CASE WHEN prev_st_id = prev_id THEN 0 ELSE 1 END) OVER (ORDER BY id) as grp
            FROM (SELECT t.*,
                         LAG(id) OVER (PARTITION BY SessionId, TransactionType ORDER BY id) as prev_st_id,
                         LAG(id) OVER (ORDER BY id) as prev_id
                  FROM Transactions t
                 ) t
           ) t
     ) t
WHERE prev_st_id <> prev_id OR prev_st_id IS NULL;

Что это делает?

  • Внутренний подзапрос вычисляет отставание идентификатора как в целом, так и по типу сеанса / транзакции.При этом используется id, потому что он кажется более стабильным, чем дата / время (в одном из столбцов есть повторяющиеся значения даты / времени).
  • Если идентификаторы отличаются, то идентифицируется новый остров.Совокупная сумма идентифицирует группы.
  • Этот grp затем используется для вычисления значений по всей группе с использованием оконных функций.
  • Затем внешний запрос просто фильтруется до первой строки вкаждая группа.

Здесь - это дБ <> скрипка.

0 голосов
/ 19 сентября 2019

Это вариант с пробелами и островками.

Я бы подошел к нему следующим образом:

1) Сначала определите и объедините группы записей.Следующий запрос дает вам минимум групп DateTimeEnd каждой группы вместе с суммой ранга

SELECT 
    SessionId, 
    TransactionType, 
    SUM(Rank) SumRank, 
    MIN(DateTimeEnd) MinDateTimeEnd
FROM (
    SELECT 
        t.*,
        ROW_NUMBER() OVER(ORDER BY DateTimeEnd) rn1,
        ROW_NUMBER() OVER(PARTITION BY SessionId, TransactionType ORDER BY DateTimeEnd) rn2
    FROM Transactions t
 ) x
GROUP BY SessionId, TransactionType, rn1 - rn2

Возвращает:

SessionId | TransactionType | SumRank | MinDateTimeEnd     
--------: | :-------------- | ------: | :------------------
        1 | Deposit         |     600 | 20/01/2017 11:16:33
        1 | Withdrawal      |     430 | 21/01/2017 11:16:33
        2 | Deposit         |     500 | 23/02/2017 11:16:33
        2 | Withdrawal      |     200 | 27/02/2017 11:16:33

2) Затем присоедините результат квышеприведенный запрос с исходной таблицей для извлечения остальных столбцов:

SELECT 
    t.id,
    t.SessionId,
    t.TransactionType,
    t.DateTimeEnd,
    t.DateStart,
    x.SumRank
FROM Transactions t
INNER JOIN (
    SELECT 
        SessionId, 
        TransactionType, 
        SUM(Rank) SumRank, 
        MIN(DateTimeEnd) MinDateTimeEnd
    FROM (
        SELECT 
            t.*,
            ROW_NUMBER() OVER(ORDER BY DateTimeEnd) rn1,
            ROW_NUMBER() OVER(PARTITION BY SessionId, TransactionType ORDER BY DateTimeEnd) rn2
        FROM Transactions t
    ) x
    GROUP BY SessionId, TransactionType, rn1 - rn2
) x 
    ON  x.SessionId = t.SessionId
    AND x.TransactionType = t.TransactionType
    AND x.MinDateTimeEnd = t.DateTimeEnd

Выход:

id | SessionId | TransactionType | DateTimeEnd         | DateStart           | SumRank
-: | --------: | :-------------- | :------------------ | :------------------ | ------:
 1 |         1 | Deposit         | 20/01/2017 11:16:33 | 20/01/2017 11:16:33 |     600
 2 |         1 | Withdrawal      | 21/01/2017 11:16:33 | 20/01/2017 11:16:33 |     430
 3 |         2 | Deposit         | 23/02/2017 11:16:33 | 23/02/2017 11:16:33 |     500
 6 |         2 | Withdrawal      | 27/02/2017 11:16:33 | 23/02/2017 11:16:33 |     200

Демонстрация на DB Fiddle

Примечание. Как отмечается в комментариях, я думаю, что ожидаемые результаты, которые вы демонстрируете, не соответствуют действительности.Строки с id s 4 и 7 не должны появляться в выходных данных, поскольку строка с идентификатором 2 имеет такие же SessionId и TransactionType и более раннюю DateTimeEnd.

...