2016-07-06 3 views
1

Это не так просто, как создание интервалов времени, длительностью N минут. Одна запись может быть 10:04, а другая 10:17, где N равно 15.Групповые записи, когда дата находится в пределах N минут

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

Я ищу наиболее «изящное» решение. Может быть, есть функция в SQL, о которой я не знал, что делает это проще.

Вот базовый сценарий, чтобы сделать ответы более совместимыми друг с другом:

create table Comparisons (
    DateField DateTime NOT NULL, 
    Amount int not null, -- default to 5 
) 

insert into Comparisons (DateField) values ('2000-01-01 10:04'),('2000-01-01 10:17'), 
('2000-01-01 12:01'),('2000-01-01 11:54'),('2000-01-01 03:02'),('2000-01-01 03:05'), 
('2000-01-01 05:02'),('2000-01-01 05:05'),('2000-01-01 05:19') 

выход ожидается:

  • мин: .. 10:04, макс: .. 10:17, сумма: 10
  • мин: .. 11:54, макс: .. 12:01, сумма: 10
  • мин: .. 3:02, макс: .. 3:05, сумма: 10
  • мин: .. 05: 0 2, max: .. 05:19, sum: 15 [optional]

Последний выход не является обязательным, но если изящное решение имеет это побочный эффект, это приемлемо. Если изящное решение не может обеспечить этот дополнительный последний выход, это не будет прерывание сделки.

+3

Редактировать свой вопрос и предоставить образцы данных и желаемые результаты. Как написано, вопрос довольно бессмыслен. –

+0

@ GordonLinoff сделано. Я уже был на нем, просто следил. – Todd

+0

Что вы будете делать, если, скажем, N = 15, и у вас есть время 10:04, 10:17 и 10:25? – ZLK

ответ

2

Я считаю, что это дает результаты, которые вы хотите:

DECLARE @Comparisons TABLE (i DATETIME, amt INT NOT NULL DEFAULT(5)); 
INSERT @Comparisons (i) VALUES ('2016-01-01 10:04:00.000') 
, ('2016-01-01 10:17:00.000') 
, ('2016-01-01 10:25:00.000') 
, ('2016-01-01 10:37:00.000') 
, ('2016-01-01 10:44:00.000') 
, ('2016-01-01 11:52:00.000') 
, ('2016-01-01 11:59:00.000') 
, ('2016-01-01 12:10:00.000') 
, ('2016-01-01 12:22:00.000') 
, ('2016-01-01 13:00:00.000') 
, ('2016-01-01 09:00:00.000'); 

DECLARE @N INT = 15; 

WITH T AS (
    SELECT i 
     , amt 
     , CASE WHEN DATEDIFF(MINUTE, previ, i) <= @N THEN 0 ELSE 1 END RN1 
     , CASE WHEN DATEDIFF(MINUTE, i, nexti) > @N THEN 1 ELSE 0 END RN2 
    FROM @Comparisons t 
    OUTER APPLY (SELECT MAX(i) FROM @Comparisons WHERE i < t.i)x(previ) 
    OUTER APPLY (SELECT MIN(i) FROM @Comparisons WHERE i > t.i)y(nexti) 
    ) 
, T2 AS (
    SELECT CASE RN1 WHEN 1 THEN i ELSE (SELECT MAX(i) FROM T WHERE RN1 = 1 AND i < T1.i) END mintime 
     , CASE WHEN RN2 = 1 THEN i ELSE ISNULL((SELECT MIN(i) FROM T WHERE RN2 = 1 AND i > T1.i), i) END maxtime 
     , amt 
    FROM T T1 
    ) 
SELECT mintime, maxtime, sum(amt) total 
FROM T2 
GROUP BY mintime, maxtime 
ORDER BY mintime; 

это, вероятно, немного clunkier, чем это могло бы быть, но это в основном просто группировка ничего в пределах @ N минут цепи.

+0

Очень хорошо написанный ответ, спасибо. Это немного неуклюжий взгляд, но если он работает, это лучше, чем одна из моих идей (цикл и настройка темп-таблицы). Это серьезный SQL с OUTER APPLY и многое другое. Этот исходный код сделает его очень легким для других, чтобы он мог также помочь ответить! – Todd

0

Похоже, вы хотите группировать записи на основе промежутков между ними, по крайней мере, < N> минут.

В SQL Server 2012+, можно использовать lag(), чтобы определить, когда группы начинают и накопленная сумма для идентификации групп:

select min(datefield), max(datefield), count(*) as num, sum(amount) 
from (select c.*, 
      sum(case when prev_datefield < dateadd(minute, -N, datefield) 
         then 1 else 0 
       end) over (order by datefield) as grp 
     from (select c.*, 
        lag(datefield) over (order by datefield) as prev_datefield 
      from Comparisons c 
      ) c 
    ) c 
group by grp; 

В более ранних версиях можно использовать подзапросы или коррелированные apply для одной и той же функциональности (хотя при гораздо худшем исполнении).

+0

очень интересно - Lag(): https://msdn.microsoft.com/en-AU/library/hh231256.aspx – Todd

+0

вместо dateadd, может потребоваться 'abs (датфик (минута, prev_datefield, TreatmentRecordDateTime)) Todd

0

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

Псевдо-код:

select * 
from Comparisons C, {-1, 0, 1} M 
group by (datediff(mi, C.DateField, 0)/N) + M 

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

Update: Этот подход не будет работать с 4-го ожидается выход [мин: .. 05:02, макс: .. 05:19, сумма: 15]

+1

Что делают фигурные скобки в SQL Server? – ErikE

+0

Это псевдокод - это буквальный набор из 3 строк. Этот ответ здесь, чтобы проработать эту идею и заставить других думать за пределами коробки. Мне не хватает знаний о функциях SQL, чтобы самостоятельно ответить на этот вопрос. Отсюда вопрос. – Todd

Смежные вопросы