Select the largest number of entries based on a date range. (which have dates within 6 months of each other)

I have a list of dates.

I need to be able to select the largest number of records that are within 6 months of each other.

Then the next largest number of records, and so on, until all records are selected.

Here are the data

1  19-Oct-2007
2  03-Dec-2007
3  16-Oct-2009
4  26-Oct-2009
5  30-Oct-2009
6  01-Nov-2009
7  16-Nov-2009
8  30-Nov-2009
9  11-Dec-2009
10  25-Dec-2009
11  01-Jan-2010
12  21-Jan-2010
13  27-Jan-2010
14  28-Jan-2010
15  28-Jan-2010
16  12-Feb-2010
17  12-Feb-2010
18  27-Feb-2010
19  09-Mar-2010
20  22-Mar-2010
21  26-Mar-2010
22  01-Apr-2010
23  22-Oct-2010
24  15-Oct-2011
25  18-Oct-2011
26  26-Oct-2011
27  16-Nov-2011
28  18-Nov-2011
29  19-Nov-2011
30  26-Nov-2011
31  29-Nov-2011
32  29-Nov-2011
33  30-Nov-2011
34  06-Dec-2011
35  16-Dec-2011
36  17-Dec-2011
37  20-Dec-2011
38  28-Dec-2011
39  01-Jan-2012
40  01-Jan-2012
41  09-Jan-2012
42  13-Jan-2012
43  27-Jan-2012
44  01-Feb-2012
45  23-Feb-2012
46  29-Feb-2012
47  01-Mar-2012
48  01-Mar-2012
49  01-Mar-2012
50  02-Mar-2012
51  04-Mar-2012
52  04-Mar-2012
53  05-Mar-2012
54  05-Mar-2012
55  17-Mar-2012
56  23-Mar-2012
57  24-Mar-2012
58  01-Apr-2012
59  03-Apr-2012
60  04-Apr-2012

One possible solution would be to choose

  • records 24-60 (they are within 172 days from each other).
  • record 23 (its not within 6 months from the previous / next date)
  • records 3-22 (they are located for 167 days from each other)
  • recods 1-2 (they are within 45 days of each other)

(I started from the highest date and chose back. This is probably not the best solution)

+3
source
4

, . , :

WITH ranked AS (
  SELECT *, rnk = ROW_NUMBER() OVER (ORDER BY Date DESC)
  FROM data
),
marked AS (
  SELECT
    rnk,
    Date,
    GroupDate = date
  FROM ranked
  WHERE rnk = 1
  UNION ALL
  SELECT
    r.rnk,
    r.Date,
    GroupDate = CASE
      WHEN m.GroupDate > DATEADD(MONTH, 6, r.Date) THEN r.Date
      ELSE m.GroupDate
    END
  FROM ranked r
  INNER JOIN marked m ON r.rnk = m.rnk + 1
)
SELECT
  MinDate     = MIN(Date),
  MaxDate     = MAX(Date),
  [RowCount]  = COUNT(*),
  RangeLength = DATEDIFF(DAY, MIN(Date), MAX(Date))
FROM marked
GROUP BY
  GroupDate
ORDER BY
  GroupDate

  • , .

  • () , .

  • , 1, .

, . , , .

, :

MinDate     MaxDate     RowCount     RangeLength
----------  ----------  -----------  -----------
2007-10-19  2007-12-03  2            45
2009-10-16  2010-04-01  20           167
2010-10-22  2010-10-22  1            0
2011-10-15  2012-04-04  37           172

script, , SQL Fiddle.

+2
select d1.date, count(*)
from dates as d1 with (nolock) 
join dates as d2 with (nolock) 
on datediff(mm,d2.date,d1.date) < 6 
group by d1.date  
order by count(*) desc 
+1

I used my own test data, this is a very complex material. The cursor may be easier to handle. But I'm not a big fan of cursors. I gave him the best shot:

declare @t table(record int, date datetime)
insert @t values(1,'19-Oct-2007'),
(2,'03-Dec-2007'),
(3,'2-may-2009'),
(4,'16-Oct-2009'),
(5,'26-Oct-2009'),
(6,'30-Oct-2009'),
(7,'01-Nov-2009'),
(8,'16-Nov-2009'),
(9,'30-Nov-2009'),
(10,'11-Dec-2009'),
(11,'11-Dec-2010'),
(12,'11-Dec-2010'),
(13,'11-Dec-2010')

;with a as
(
  select datediff(day, t1.date, t2.date) daysapart, 
  row_number() over (order by count desc) rn,
  b.count, 
         t1.record fromrecord, 
         t2.record torecord
  from @t t1
  join @t t2
  on t1.date <= t2.date 
     and dateadd(month, 6, t1.date) > t2.date 
     and t1.record <= t2.record
  cross apply (select count(*) count from @t where record between t1.record and t2.record) b
)
, b as
(
    select * from a where not exists 
    (select 1 from a b where (a.fromrecord between b.fromrecord and b.torecord
      or a.torecord between b.fromrecord and b.torecord)
      and a.rn > b.rn and not exists(select 1 from a c where 
      (b.fromrecord between c.fromrecord and c.torecord
      or b.torecord between c.fromrecord and c.torecord)
      and b.rn > c.rn))
)
select count, fromrecord, torecord, daysapart from b

Result:

count       fromrecord  torecord    daysapart
----------- ----------- ----------- -----------
7           4           10          56
3           11          13          0
2           1           2           45
1           3           3           0
+1
source

I reviewed

  • 6Months = 180 days
  • If the difference between the dates is> 180 days, they will be included in the list.

Try the following:

create table #list (id int, dt datetime  )
-- insert you data into #list

select s1.id as ID_1, s1.dt as Date_2 , s2.id as ID_2, s2.dt as Date_2 
,abs( datediff(day, s2.dt, s1.dt) ) diff_in_days
from #list s1 , #list s2 
order by  case when abs(datediff(day, s2.dt, s1.dt) ) > 180 then 1
else  abs(datediff(day, s2.dt, s1.dt)) end  desc 
0
source

All Articles