如何按DateRange对记录进行分组

时间:2012-02-09 12:19:17

标签: sql sql-server sql-server-2005 sql-server-2008-r2

如何按日期范围在sql中对记录进行分组?

考虑此表结构。

 Key       ID       VISITDATE
 1         1        2011-01-07
 2         1        2011-01-09
 3         2        2011-01-10
 4         1        2011-01-12
 5         3        2011-01-12
 6         1        2011-01-18
 7         2        2011-01-21
 9         1        2011-02-28
 10        2        2011-03-21
 11        1        2011-01-06
 12        1        2011-02-29

我需要获取出现的次数。如果您在10天内对同一个ID进行两次访问,那么只应计算一次访问次数。 所以给出id为1 = 3(01 / 06,01 / 18,02 / 28)的出现次数的例子

非常感谢递归CTE查询。

3 个答案:

答案 0 :(得分:1)

您没有说明如何定义日期范围

如果从今天开始范围,您可以这样做:

SELECT     id, COUNT(DISTINCT DATEDIFF(dd, visitdate, GETDATE()) / 10) AS Expr1
FROM         test
GROUP BY id

如果你想从数据的最新日期开始范围,你可以这样做

DECLARE @maxdate as datetime
SET @maxdate = (select MAX(visitdate) from test)

SELECT     id, COUNT(DISTINCT DATEDIFF(dd, visitdate, @maxdate) / 10) 
FROM         test
GROUP BY id

像这样的查询将为@Dems指向

的情况提供技巧
SELECT id, count(DISTINCT visitdate)
FROM (
    SELECT   id, visitdate,
        (SELECT MAX(visitdate) 
        FROM test AS t 
        WHERE t.id = test.id AND t.visitdate<test.visitdate) AS prev_date
    FROM     test ) as temp
WHERE DATEDIFF(dd, prev_date, visitdate) > 10 OR prev_date IS NULL
GROUP BY id

上述查询不计算具有相同ID的记录,并且其中存在另一条记录,其日期小于十天

如果您想从每个ID的最小日期开始分割日历,您可以执行以下操作

SELECT     t.id, COUNT(DISTINCT DATEDIFF(dd, mindate, visitdate) / 10) 
FROM         test as t JOIN 
  (SELECT id, MIN(visitdate) AS mindate FROM test GROUP BY id) as mindates
    ON t.id = mindates.id
GROUP BY t.id

答案 1 :(得分:1)

我知道您要求递归CTE但没有SQL Server 2012中的新窗口增强功能(FIRST_VALUE()等)我认为当你必须跟踪不是这样时,这将是一个熊只有前一行而且前面的行同时出现。这是一个我认为可以达到你想要的光标版本:

DECLARE @f TABLE([Key] INT, ID INT, VISITDATE DATE);

INSERT @f VALUES
  (1 ,1,'2011-01-07'), (2 ,1,'2011-01-09'), (3 ,2,'2011-01-10'), (4 ,1,'2011-01-12'),
  (5 ,3,'2011-01-12'), (6 ,1,'2011-01-18'), (7 ,2,'2011-01-21'), (9 ,1,'2011-02-28'), 
  (10,2,'2011-03-21'), (11,1,'2011-01-06'), (12,1,'2011-03-01');

DECLARE @ID INT, @dt DATE;

DECLARE @result TABLE(ID INT, FirstDate DATE, VisitCount INT);

DECLARE c CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY
    FOR SELECT ID, VISITDATE FROM @f ORDER BY ID, VISITDATE;

OPEN c;

FETCH NEXT FROM c INTO @ID, @dt;

WHILE @@FETCH_STATUS = 0
BEGIN
    IF NOT EXISTS 
    (
        SELECT 1 FROM @result WHERE ID = @ID
            AND DATEDIFF(DAY, FirstDate, @dt) <= 10 -- maybe < 10?
    )
    INSERT @result SELECT @ID, @dt, 1;

    FETCH NEXT FROM c INTO @ID, @dt;
END

SELECT ID, FirstDate FROM @result;

SELECT ID, VisitCount = COUNT(*) FROM @result GROUP BY ID;

CLOSE c;
DEALLOCATE c;

结果:

ID          FirstDate
----------- ----------
1           2011-01-06
1           2011-01-18
1           2011-02-28
2           2011-01-10
2           2011-01-21
2           2011-03-21
3           2011-01-12


ID          VisitCount
----------- -----------
1           3
2           3
3           1

是的,我知道你总是被警告不要与陌生人交谈并远离游标,但在某些情况下,他们是最直接的解决方案(并且有时可以比多次扫描更好地执行基于集合的解决方案可能会招致)。

答案 2 :(得分:0)

创建一个标量函数,该函数返回给定范围内所有日期的相同日期,然后按其分组。