Question

I have a table that looks like this:-

tblMeterReadings

id  meter        date        total
1      1      03/01/2014  100.1
1      1      04/01/2014  184.1
1      1      05/01/2014  134.1
1      1      06/01/2014  132.1
1      1      07/01/2014  126.1
1      1      08/01/2014  190.1

This is an 8 day "contiguous block" from '2014-01-03' to '2014-01-08'.

In the real table there are "contiguous blocks" of years in length.

I need to select the MOST RESCENT CONTINUOUS 365 DAY BLOCK (filtered by meter column). If 365 cannot be found, then it should select next largest continuous block.

When I say CONTINUOUS I mean there must be no days missing.

This is beyond me, so if someone can solve... I will be very impressed.

Was it helpful?

Solution

using distinct to not count days with 2 sets of data

declare @gapdays int = 2 -- replace this with 365 in your case

;with x as
(
  select datediff(d, '2014-01-01', [date])-dense_rank()over(order by [date])  grp
         ,[date]
  from @t
)
select top 1 max([date]) last_date, min([date]) first_date, count(distinct [date]) days_in_a_row
from x
group by grp
having count(distinct [date]) >= @gapdays
order by max([date]) desc

OTHER TIPS

There you go:

declare @tblMeterReadings table (id int, meter int, [date] date, total money)

insert into @tblMeterReadings ( id, meter, date, total )
values
    (1, 1, '03/01/2014', 100.1),
    (1, 1, '04/01/2014', 184.1),
    (1, 1, '05/01/2014', 134.1),
    (1, 1, '06/01/2014', 132.1),
    (1, 1, '07/01/2014', 126.1),
    (1, 1, '08/01/2014', 190.1),
    (1, 1, '10/01/2014', 200.1),
    (1, 1, '12/01/2014', 202.1),
    (1, 1, '13/01/2014', 204.1)

;with data as (
    select i = datediff(day, '2014', [date]), * 
    from @tblMeterReadings l
)
, islands as (
    select island = l.i - row_number() over (order by i), l.*
    from data l 
)
, spans as (
    select l = min(i), r = max(i)
    from islands i
    group by island
)
select * 
from spans s
left join data l on s.l = l.i
left join data r on s.r = r.i

Most recent continuous block not exceeding 365 days in length will be as follows:

select top 1 *
from spans s
left join data l on s.l = l.i
left join data r on s.r = r.i
where s.l - s.r < 365
order by s.l - s.r desc, s.r desc

With recursive CTE and datepart(dayofyear, date):

with cte as
(
  select id, meter, date, datepart(dayofyear, date) as x, cast(1 as int) as level, t1.date as startDate from tblMeterReadings t1
    where meter = 1 
      and not exists(select * from tblMeterReadings t2 where (datepart(dayofyear, t1.date) - 1) = datepart(dayofyear, t2.date))
  union all
  select t1.id, t1.meter, t1.date, datepart(dayofyear, t1.date) as x, t2.level + 1, t2.startDate from tblMeterReadings t1
   inner join cte t2 ON (datepart(dayofyear, t1.date)) = (datepart(dayofyear, t2.date) + 1)      
)    
select TOP 365 * from cte
where cte.startDate = (select top 1 startdate 
                       from cte 
                       --where Level <= 365 
                       order by Level desc, startDate desc)
order by Level desc
OPTION ( MAXRECURSION 365 )

SQL Fiddle example

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top