2009-12-09 3 views
2

В общем, мне нужно связать записи (группы), созданные за аналогичные периоды времени. Если это поможет, подумайте о нижеследующем примере как данные по потоку данных, где нет идентификатора сеанса, и мне нужно создать эти сеансы.Групповые события по временному расстоянию в SQL

У меня есть следующий набор данные:

UserId INT, 
EventId INT, 
DateCreated DATETIME, 
BlockId INT 

Примут следующие данные:

{123, 111, '2009-12-01 9:15am', NULL} 
{123, 222, '2009-12-01 9:20am', NULL} 
{123, 333, '2009-12-01 9:25am', NULL} 
{123, 444, '2009-12-03 2:30pm', NULL} 
{123, 555, '2009-12-03 2:32pm', NULL} 

Что мне нужно сделать, это разделить эти события вверх, пользователем, в височные ведра. Существует бизнес-правило, в котором говорится, что все> 30 минут должны быть новым ковшом. В приведенном выше примере события 111-333 представляют собой блок, т. Е. Не более 30 минут отделяет их. Аналогично, события 444-555 представляют собой второй блок.

Мое текущее решение использует курсор и чрезвычайно медленное (следовательно, неустойчиво для количества данных, которые мне нужно обработать). Я могу опубликовать код, но это довольно просто.

Любые идеи?

+0

Я не думаю, что я понимаю, что вам нужно - какими будут ваши результаты для образцов данных? – Ray

+0

В ваших образцах данных неверная метка времени для 'UserID' 123ID' UserID' 555 - это два дня слишком рано. – pilcrow

+0

Исправлена ​​метка времени, хороший улов. – austincav

ответ

1

основы нити комментария,

A. Ковши определяются первой записью в ведре, и первая запись в каждом ковшоме определяются как любая строка, где DateCreated составляет более 30 минут после того, как последние ранее Дата создания. (сразу же предыдущая запись)

B. Остальные строки в ведре - все строки с DateCreated на или после первой строки, DateCreated которой составляет менее 30 минут после сразу предыдущей строки, и там не существует -qualifying (или новая определяющая ведро) строка с указанной строки, определяющей Bucket.

На английском языке:

Выберите DateCreated этих записей wheret он DateCreated более чем на 30 минут после предыдущего DateCreated и агрегатной функции вашего выбора на всех остальных записей в таблице, чьи DateCreated это после того, ковшового определения datecreated, менее чем через 30 минут после его немедленного предыдущего DateCreated, и нет записей между определяющим ковш DateCreated и этим, который следует за 30-минутным промежутком.

В SQL:

Select Z.BucketDefinitionDate , Count(*) RowsInBucket 
    From (Select Distinct DateCreated BucketDefinitionDate 
     From Table Ti 
     Where DateCreated > DateAdd(minute, 30, 
      (Select Max(DateCreated) From Table 
      Where DateCreated < Ti.DateCreated))) Z 
    Join Table B 
     On B.DateCreated > Z.BucketDefinitionDate 
      And Not Exists 
      (Select * From Table 
       Where DateCreated Between Z.BucketDefinitionDate 
            And B.DateCreated 
       And DateCreated > DateAdd(minute, 30, 
        (Select Max(DateCreated) From Table 
         Where DateCreated < B.DateCreated))) 
    Group By Z.BucketDefinitionDate 
+0

Я бы не отправлял сообщения, если это решение будет работать :-) Предостережение состоит в том, что события могут (а) встречаться в смежных блоках более 30 минут и (b) блоки могут пересекать границы 30 минут – austincav

+0

Вы должны иметь некоторый набор основанного на том, что определяет «ведро» или «блок», который определит ведро ... Что это за правило –

+0

В вашем вопросе говорится: «Это бизнес-правило, в котором говорится что-либо» 30 минут должно быть новым ведром «Что от этого начинается «30 минут»? Предыдущая строка? Так что, если есть строка каждые 29 минут, все они будут падать в том же ведре? или из первого ряда в ковше? или из последней строки в предыдущем ковше? –

0

Что вы можете попробовать это

DECLARE @TABLE TABLE(
     ID INT, 
     EventID INT, 
     DateCreated DATETIME 
) 

INSERT INTO @TABLE SELECT 123, 111, '2009-12-01 9:15am' 
INSERT INTO @TABLE SELECT 123, 222, '2009-12-01 9:20am' 
INSERT INTO @TABLE SELECT 123, 333, '2009-12-01 9:25am' 
INSERT INTO @TABLE SELECT 123, 444, '2009-12-03 2:30pm' 
INSERT INTO @TABLE SELECT 123, 555, '2009-12-01 2:32pm' 

SELECT ID, 
     DATEADD(dd, DATEDIFF(dd,0,DateCreated), 0) DayVal, 
     DATEPART(hh, DateCreated) HourPart, 
     FLOOR(DATEPART(mi, DateCreated)/30.) MinBucket 
FROM @TABLE 

Теперь вы можете группировать по DayVal, HourPart и MinBucket.

+0

См. Примечание к предыдущему ответу, я думаю, что ваше решение страдает от одной и той же проблемы. – austincav

+0

Вы обнаружите, что этот bucketing EXTEMELY сложный, как только события появятся на расстоянии 5 минут друг от друга. Как вы решаете, где начинать заданное ведро и где заканчиваться? Это затем вернется к запросу курсора ... –

+0

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

1

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

;WITH StartTimes AS 
(
SELECT DISTINCT 
    T1.DateCreated AS StartTime 
FROM 
    My_Table T1 
LEFT OUTER JOIN My_Table T2 ON 
    T2.UserID = T1.UserID AND 
    T2.EventID = T1.EventID AND 
    T2.DateCreated >= DATEADD(mi, -30, T1.DateCreated) AND 
    T2.DateCreated < T1.DateCreated 
WHERE 
    T2.UserID IS NULL 
) 
SELECT 
    StartTimes.StartTime, 
    EndTimes.EndTime 
FROM 
(
SELECT DISTINCT 
    T3.DateCreated AS EndTime 
FROM 
    My_Table T3 
LEFT OUTER JOIN My_Table T4 ON 
    T4.UserID = T3.UserID AND 
    T4.EventID = T3.EventID AND 
    T4.DateCreated <= DATEADD(mi, 30, T3.DateCreated) AND 
    T4.DateCreated > T3.DateCreated 
WHERE 
    T4.UserID IS NULL 
) AS ET 
INNER JOIN StartTimes ST ON 
    ST.StartTime <= ET.EndTimes 
LEFT OUTER JOIN StartTimes ST2 ON 
    ST2.StartTime <= ET.EndTimes AND 
    ST2.StartTime > ST.StartTime 
WHERE 
    ST2.StartTime IS NULL 
+0

Вы можете быть здесь! дайте мне знать и вернитесь к вам. Спасибо чувак. – austincav

0

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

declare @table table(
    id int identity(1,1), 
    userId int, 
    eventId int,dateCreated datetime, 
    bucket int 
) 

insert into @table select 123, 111, '2009-12-01 9:15am', 0 
// etc... insert more rows - note that the 'bucket' field is set to 0 

declare @next_bucket int 
set @next_bucket = 1 
update @table 
    set bucket = @next_bucket, @next_bucket = @next_bucket + 1 
    from @table as [current] 
    where datecreated > dateadd(mi, 30, (select datecreated from @table as previous where [current].id = previous.id + 1)) 

update @table 
    set bucket = 
     coalesce((select max(bucket) 
       from @table as previous 
       where previous.id < [current].id 
        and bucket <> 0 
     ), 1) 

    from @table as [current] 
    where bucket = 0 

-- return the results 
select * from @table