按连续日期和计数分组

时间:2015-11-24 18:30:33

标签: sql tsql

我有一个表格,其中包含与Date一起访问的报告的信息。我需要根据日期范围对报告进行分组并计算它们。

我正在使用T-SQL

EventId   ReportId   Date
60          4         11/24/2015
59          11        11/23/2015
58          6         11/22/2015
57          11        11/22/2015
56          9         11/21/2015
55          3         11/20/2015
54          5         11/20/2015
53          6         11/19/2015
52          5         11/19/2015 
51          4         11/18/2015
50          3         11/17/2015
49          9         11/16/2015

如果天数差异为3,那么我需要格式为

的结果
StartDate    EndDate     ReportsAccessed
11/22/2015   11/24/2015    4
11/19/2015   11/21/2015    5
11/16/2015   11/18/2015    3

但天数之间的差异可能会发生变化。

3 个答案:

答案 0 :(得分:4)

假设您拥有所有日期的值,则可以计算每个日期与最大(或最小)日期之间的天数差异。然后将其除以3并将其用于聚合:

select min(date), max(date), count(*) as ReportsAccessed
from (select t.*, max(date) over () as maxd
      from table t
     ) t
group by (datediff(day, date, maxd) / 3)
order by min(date);

“3”是我认为你所指的“日间差异”。

答案 1 :(得分:1)

这2个区块只是为了更清楚地说明您需要更改的参数

DECLARE @t as TABLE(
id int identity(1,1),
reportId int,
dateAccess date)

DECLARE @NumberOfDays int=3;

这是实际的选择

Select StartDate, EndDate, COUNT(reportId) from 
(
    select *, 
        DATEADD(day, DATEDIFF(DAY, dateAccess, maxdate.maxdate)%@NumberOfDays, dateAccess) as EndDate,
        DATEADD(day, DATEDIFF(DAY, dateAccess, maxdate.maxdate)%@NumberOfDays-@NumberOfDays+1, dateAccess) as StartDate
    from @t, (select MAX(dateAccess) maxdate from @t t2) maxdate
) results
GROUP BY StartDate, EndDate
ORDER BY StartDate desc 

有些地方我不确定它是否已经过优化,例如使用select max(date)交叉连接而不是使用子查询,但是会返回OP的确切结果。

基本上,我只是根据条目与MAX(date)的距离将条目分组,然后使用COUNT。在那个注释中,使用COUNT(distinct ...)可能更有用,否则如果有人查看文档#9 3次,它会告诉你3个文档被检查,但只有1个真正被查看过。

使用MAX(date)超过MIN(date)的好处是,您的第一个群组将始终拥有最长的天数。如果您想将最后几个时段与平均值进行比较,这将非常有用。缺点是您没有稳定的数据。对于每个新条目(假设它是新的一天),您的查询将自行循环以生成一组新结果。如果你想绘制数据图表,你可以更好地与MIN(日期)进行比较,这样当你添加新数据时,前几天不会改变。

根据用途,推断上一期间完成的访问次数甚至可能有用(在这种情况下,MIN(date)也是优选的。)

以下是戈登答案的改编,可能很多更优化(至少更美观):

SELECT DateADD(day, -datediff(day, dateAccess, maxdate)/3*3, maxdate) as EndDate, 
       DateADD(day, (-datediff(day, dateAccess, maxdate)/3+1)*3, maxdate) as StartDate, 
       count(reportId)
from (select *, MAX(dateAccess) over() as maxdate from @t) t
GROUP BY datediff(day, dateAccess, maxdate)/3, maxdate

答案 2 :(得分:0)

我会坚持认为最有效的方法是使用计数表。通过这种方式,您可以从日期列的索引中获得所有优势:

declare @c int = 3

;with minmax as(select min(date) as mind, max(date) as maxd from t),
tally as(select @c * (-1 + row_number() over(order by(select null))) as rn 
         from master..spt_values),
intervals as(select dateadd(dd, rn, mind) as f, dateadd(dd, rn + @c - 1, mind) t
             from tally t cross join minmax m where dateadd(dd, rn, mind) <= maxd)
select i.f as [from], i.t as [to], count(*) as reeports
from intervals i
join t on t.date >= i.f and t.date <= i.t
group by i.f, i.t

说明:minmax从表格中选择最短日期和最长日期。 tally生成从0到N的数字(取决于系统,但是会计算到计算间隔)。 intervals选择生成的时间间隔。最后一部分是间隔的简单连接,以计算每个间隔的计数。

小提琴http://sqlfiddle.com/#!3/c61d1/5