Nano Hash - криптовалюты, майнинг, программирование

Рекурсивно группировать записи в SQL на основе минут между ними

У меня есть набор данных, аналогичный приведенному ниже образцу:

RecordId  RecordDateTime      AttributeA  AttributeB  Amount Value
--------- ------------------- ----------- ----------- ------ -------
1         2020-08-01 00:00:00 AttA01      AttB01      10.00   6510
2         2020-08-01 00:00:00 AttA01      AttB01      11.00   3620
3         2020-08-01 00:07:00 AttA01      AttB01      17.00  87430
4         2020-08-01 00:15:00 AttA01      AttB01      15.00   4140
5         2020-08-01 00:20:00 AttA02      AttB01      30.00   2350
6         2020-08-01 00:30:00 AttA01      AttB01      20.00  14560
7         2020-08-01 00:46:00 AttA01      AttB01      15.00   1370
8         2020-08-01 02:15:00 AttA01      AttB01      30.00   1380
9         2020-08-01 02:30:00 AttA01      AttB01       5.00   4490
10        2020-08-01 02:30:00 AttA01      AttB01       6.00  13100

Мне нужно сгруппировать все записи, в которых AttributeA и AttributeB совпадают, а время записи RecordDateTime для следующей записи находится в пределах 15 минут от значения RecordDateTime текущей записи.

Результаты должны показывать самую раннюю дату группы, AttributeA и AttributeB, сумму Amount и самой ранней записи в значении группы как StartValue и значение последней записи как EndValue.

Пример результатов:

RecordDateTime      AttributeA  AttributeB  Amount StartValue   EndValue
------------------- ----------- ----------- ------ ---------- ----------
2020-08-01 00:00:00 AttA01      AttB01      73.00        3620      14560
2020-08-01 00:20:00 AttA02      AttB01      30.00        2350       2350
2020-08-01 00:46:00 AttA01      AttB01      15.00        1370       1370
2020-08-01 02:15:00 AttA01      AttB01      41.00        1380      13100

Я борюсь с рекурсивной группировкой, основанной на разнице во времени в 15 минут. Я пробовал несколько вещей, в том числе рекурсивный CTE, но на самом деле не добился никакого прогресса, который стоило бы показать. Есть ли простое решение для группировки?

Изменить: предоставление дополнительных сведений по запросу. Для приложения всегда будет начало и конец даты, которые пользователь может динамически выбирать, что может охватывать несколько месяцев, начиная с любого дня или времени.

Предоставленные данные являются производственными данными. В реальной жизни AttributeA будет именем человека, а AttributeB — местоположением, а записи — транзакциями.

Любая серия транзакций, совершенных одним и тем же лицом в одном и том же месте с задержкой от 0 до 15 минут (›= 0 и ‹= 15) между каждой транзакцией, должна быть сгруппирована, суммирована и рассмотрена как одна транзакция. Другими словами, если транзакция находится в пределах 15 минут от предыдущей транзакции, она должна быть сгруппирована с предыдущими транзакциями и любой другой предшествующей транзакцией, с которой сгруппирована предыдущая транзакция.

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


Ответы:


1

Это небольшой поворот в проблеме островов.

Учитывая эти данные выборки:

CREATE TABLE #x
(
  RecordId int, 
  RecordDateTime datetime, 
  AttributeA char(6), 
  AttributeB char(6), 
  Amount DECIMAL(12,2), 
  [Value] int
);

INSERT #x(RecordId, RecordDateTime, AttributeA, AttributeB, Amount, [Value])
VALUES
  (1,  '2020-08-01 00:00:00', 'AttA01', 'AttB01', 10.00,   6510),
  (2,  '2020-08-01 00:00:00', 'AttA01', 'AttB01', 11.00,   3620),
  (3,  '2020-08-01 00:07:00', 'AttA01', 'AttB01', 17.00,  87430),
  (4,  '2020-08-01 00:15:00', 'AttA01', 'AttB01', 15.00,   4140),
  (5,  '2020-08-01 00:20:00', 'AttA02', 'AttB01', 30.00,   2350),
  (6,  '2020-08-01 00:30:00', 'AttA01', 'AttB01', 20.00,  14560),
  (7,  '2020-08-01 00:46:00', 'AttA01', 'AttB01', 15.00,   1370),
  (8,  '2020-08-01 02:15:00', 'AttA01', 'AttB01', 30.00,   1380),
  (9,  '2020-08-01 02:30:00', 'AttA01', 'AttB01',  5.00,   4490),
  (10, '2020-08-01 02:30:00', 'AttA01', 'AttB01',  6.00,  13100);

Я разделил это на три CTE:

  • x определяет, какие строки являются якорными, находя любую строку с временной отметкой, прошедшую более чем на 15 минут по сравнению с предыдущей строкой для той же комбинации A/B (или с начала 2000 года в случае первого экземпляра любого A/ комбо В). Он назначает столбец с именем is_anchor, чтобы легко идентифицировать эти строки.
  • y просто применяет группу путем суммирования всех is_anchor значений из всех предыдущих строк для данной комбинации A/B, упорядоченных по времени. Эффект здесь заключается в том, что все строки, следующие за привязкой, которая не является привязкой, назначаются одной и той же группе, потому что все они добавляют 0 к сумме.
  • z затем для каждой группы суммирует значения Amount и находит первое и последнее Value. Из z мы выбираем только строки, где is_anchor = 1.

Результирующий запрос:

;WITH x(a,b,d,am,v,is_anchor) AS 
(
  SELECT AttributeA, AttributeB, RecordDateTime, Amount, [Value],
      CASE WHEN DATEDIFF(MINUTE, COALESCE(LAG(RecordDateTime, 1) 
      OVER 
      (
        PARTITION BY AttributeA, AttributeB ORDER BY RecordDateTime
      ), '20000101'), RecordDateTime) > 15 THEN 1 ELSE 0 END
  FROM #x
),
y AS 
(
  SELECT *, 
    grp = SUM(is_anchor) OVER 
          (PARTITION BY a,b ORDER BY d ROWS UNBOUNDED PRECEDING)
  FROM x
),
z AS 
(
    SELECT d, a, b, is_anchor,
      Amount = SUM(am) OVER (PARTITION BY a,b,grp), 
      StartValue = FIRST_VALUE(v) OVER 
                   (PARTITION BY a,b,grp ORDER BY d, v),
      EndValue = FIRST_VALUE(v) OVER 
                   (PARTITION BY a,b,grp ORDER BY d DESC, v DESC)
    FROM y
)
SELECT RecordDateTime = d, AttributeA = a, AttributeB = b,
       Amount, StartValue, EndValue
FROM z
WHERE is_anchor = 1
ORDER BY d, a, b;

Полученные результаты:

RecordDateTime           AttributeA  AttributeB  Amount  StartValue  EndValue
=======================  ==========  ==========  ======  ==========  ========
2020-08-01 00:00:00.000  AttA01      AttB01      73.00         3620     14560
2020-08-01 00:20:00.000  AttA02      AttB01      30.00         2350      2350
2020-08-01 00:46:00.000  AttA01      AttB01      15.00         1370      1370
2020-08-01 02:15:00.000  AttA01      AttB01      41.00         1380     13100

dbfiddle

25.08.2020
  • Это решение отлично работает и как бы изменило мой подход к проблемам SQL в целом. Просто чтобы прояснить правила для начального и конечного значений, когда есть привязка даты для StartValue, нам нужно выбрать наименьшее значение, а когда есть привязка даты для EndValue, нам нужно выбрать наибольшее значение. 26.08.2020
  • @Corvex111 Corvex111 Обновил логику FIRST_VALUE() и добавил EndValue, которую я почему-то пропустил в первый раз. 26.08.2020
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..