按时间跨度对行进行分组

时间:2015-07-15 19:56:15

标签: sql-server

需要计算一对对象在给定时间间隔内一起出现的次数。如果相同的对象在5分钟跨度内被多次列出,则应该计为一次出现。我有代码来计算每个单独的事件对,但无法弄清楚如何对在5分钟内出现的多个事件进行分组:

if object_id(N'a', N'U') IS NOT NULL
    DROP TABLE a;
GO 

create table a (name varchar (20), dt datetime);
go

create index i1 on a (name);

insert a 
values 
  ('A', '20140101 13:00:00.000')
, ('A', '20140101 13:00:01.000')
, ('A', '20140101 13:00:02.000')
, ('B', '20140101 13:01:00.000')
, ('C', '20140101 13:01:30.000')
, ('D', '20140101 13:02:00.000')
, ('E', '20140101 13:02:30.000')

, ('B', '20140201 13:00:00.000')
, ('C', '20140201 13:01:00.000')
, ('K', '20140201 13:01:30.000')
, ('L', '20140201 13:02:00.000')
, ('M', '20140201 13:02:30.000')
, ('A', '20140201 13:03:00.000')

, ('A', '20140301 13:00:00.000')
, ('D', '20140301 13:01:00.000')
, ('E', '20140301 13:01:30.000')
, ('P', '20140301 13:02:00.000')
, ('R', '20140301 13:02:30.000')
, ('Q', '20140301 13:03:00.000')

, ('A', '20140401 13:00:00.000')
, ('X', '20140401 13:01:00.000')
, ('Y', '20140401 13:01:30.000')
, ('Z', '20140401 13:02:00.000')
GO

with prox (FirstName, SecondName)
    AS (select a.name, b.name
        from a
        cross join a AS b
        where a.name < b.name
        and ABS(DATEDIFF(mi, a.dt, b.dt)) < 5)

select  FirstName, SecondName, count(*)
from    prox
group by FirstName, SecondName
having count(*) > 1
order by 1, 2

在我的例子中,AB,AC,AD和AE的计数应该是2而不是4,因为在第一组中,A在5分钟内出现3次,应该计数一次而不是3次。

1 个答案:

答案 0 :(得分:1)

你可以创建另一个cte,只获得至少5分钟的值,然后再使用你的cte

WITH    cte
    AS (SELECT
            name,
            dt
        FROM
            a a1
        WHERE
            dt = (SELECT MIN (dt) FROM a a2 WHERE a2.name = a1.name
                )
        UNION ALL
        SELECT
            a1.name,
            a1.dt
        FROM
            a a1
        JOIN cte a2 ON a1.name = a2.name
                        AND a1.dt > DATEADD(minute,5,a2.dt)
        ),
cteGroup 
    AS (SELECT DISTINCT
            name,
            dt
        FROM 
            cte
    ),  

prox(FirstName,SecondName)
    AS (SELECT
            a.name,
            b.name
        FROM
            cteGroup AS a
        CROSS JOIN cteGroup AS b
        WHERE
            a.name < b.name
            AND ABS(DATEDIFF(mi,a.dt,b.dt)) < 5
        )
SELECT
    FirstName,
    SecondName,
    COUNT(*)
FROM
    prox
GROUP BY
    FirstName,
    SecondName
HAVING
    COUNT(*) > 1
ORDER BY
    1,
    2

SQL Fiddle