Reputation: 33
I need to find the start and end dates of range defined as: start date is the first date and the end date is the first date where the subsequent date is two months or more after the end date. There can be multiple possible ranges
I have a table structure like:
ID int identity(1,1),
fk_ID char(9),
dateField datetime
The data looks like:
1 a 2012-01-01
2 a 2012-01-05
3 a 2012-01-12
4 b 2012-02-01
5 a 2012-04-01
6 b 2012-05-01
7 a 2012-05-30
The expected output would look like:
fk_id startdate enddate
a 2012-01-01 2012-01-12
a 2012-04-01 2012-05-30
b 2012-02-01 2012-02-01
b 2012-05-01 null
EDIT: By doing the following:
CREATE TABLE #temp
(
autonum int identity(1,1),
id char(9),
sd datetime
)
insert into #temp (id, sd) values ('a', '2012-01-01')
insert into #temp (id, sd) values ('a', '2012-01-05')
insert into #temp (id, sd) values ('a', '2012-01-12')
insert into #temp (id, sd) values ('a', '2012-03-01')
insert into #temp (id, sd) values ('a', '2012-04-03')
insert into #temp (id, sd) values ('a', '2012-06-06')
insert into #temp (id, sd) values ('b', '2012-02-12')
insert into #temp (id, sd) values ('b', '2012-02-15')
insert into #temp (id, sd) values ('b', '2012-03-01')
insert into #temp (id, sd) values ('b', '2012-04-03')
insert into #temp (id, sd) values ('b', '2012-06-01')
select t1.id, null as previousend, min(t1.sd) as nextstart
from #temp t1
group by t1.id
union
select t1.id, t1.sd as enddate, (select min(t2.sd) from #temp t2 where t1.id=t2.id and t2.sd>t1.sd) as nextstart
from #temp t1
where (select min(t2.sd) from #temp t2 where t1.id=t2.id and t2.sd>t1.sd) >= dateadd(month, 2, t1.sd)
union
select t1.id, max(t1.sd), null
from #temp t1
group by t1.id
drop table #temp
I can get output like this:
id previousend nextstart
--------- ----------------------- -----------------------
a NULL 2012-01-01 00:00:00.000
a 2012-04-03 00:00:00.000 2012-06-06 00:00:00.000
a 2012-06-06 00:00:00.000 NULL
b NULL 2012-02-12 00:00:00.000
b 2012-06-01 00:00:00.000 NULL
Which is very close, but ideally the start and end date of the range would be on the row.
Upvotes: 2
Views: 983
Reputation: 280625
Here is my best guess given all the changes to the question. I still find the problem very confusing, splintered and that the desired results for the two cases don't seem to match. With this query:
;WITH x AS
(
SELECT a.id, sd = a.sd, ed = b.sd, rn1 = ROW_NUMBER() OVER
(PARTITION BY a.id, a.sd ORDER BY a.sd)
FROM #temp AS a
LEFT OUTER JOIN #temp AS b
ON a.id = b.id
AND b.sd >= a.sd
AND b.sd <= DATEADD(MONTH, 2, a.sd)
),
y AS
(SELECT id, sd,
ed = (SELECT MAX(ed) FROM x AS x2
WHERE x.id = x2.id AND x2.sd <= DATEADD(MONTH, 2, x.sd)
)
FROM x
WHERE rn1 = 1
),
z AS
(
SELECT id, sd = MIN(sd), ed
FROM y GROUP BY id, ed
)
SELECT id, sd, ed /* = CASE
WHEN ed > sd OR (sd = ed AND NOT EXISTS
(SELECT 1 FROM z AS z2 WHERE z2.id = z.id AND z.sd > z2.sd)) THEN ed END
*/
FROM z
ORDER BY id, sd;
The results for your first set of data:
INSERT #temp (id, sd) VALUES
('a','2012-01-01'),
('a','2012-01-05'),
('a','2012-01-12'),
('b','2012-02-01'),
('a','2012-04-01'),
('b','2012-05-01'),
('a','2012-05-30');
Is as follows:
id sd ed
a 2012-01-01 2012-01-12
a 2012-04-01 2012-05-30
b 2012-02-01 2012-02-01
b 2012-05-01 2012-05-01
And for the second set:
insert into #temp (id, sd) values ('a', '2012-01-01')
insert into #temp (id, sd) values ('a', '2012-01-05')
insert into #temp (id, sd) values ('a', '2012-01-12')
insert into #temp (id, sd) values ('a', '2012-03-01')
insert into #temp (id, sd) values ('a', '2012-04-03')
insert into #temp (id, sd) values ('a', '2012-06-06')
insert into #temp (id, sd) values ('b', '2012-02-12')
insert into #temp (id, sd) values ('b', '2012-02-15')
insert into #temp (id, sd) values ('b', '2012-03-01')
insert into #temp (id, sd) values ('b', '2012-04-03')
insert into #temp (id, sd) values ('b', '2012-06-01')
Is as follows:
id sd ed
a 2012-01-01 2012-04-03
a 2012-06-06 2012-06-06
b 2012-02-12 2012-06-01
If you uncomment the CASE block you'll get NULLs for the end date where the start date and end date are the same. As I suggested multiple times, your question is splintered and your desired results don't seem to match, so I'm not sure what the right answer is.
Upvotes: 2
Reputation: 35605
attempt number two which is on Fiddle and is far from elegant but seems to work apart from the final record not being NULL for the end date:
CREATE TABLE temp
(
id char(9),
d datetime
);
insert into temp (id, d) values ('a', '2012-01-01');
insert into temp (id, d) values ('a', '2012-01-05');
insert into temp (id, d) values ('a', '2012-01-12');
insert into temp (id, d) values ('a', '2012-04-01');
insert into temp (id, d) values ('a', '2012-05-30');
insert into temp (id, d) values ('b', '2012-02-01');
insert into temp (id, d) values ('b', '2012-05-01');
SELECT
x.id ,
min(x.sd) sd ,
x.ed
FROM
(SELECT
a.id ,
a.sd ,
max(a.ed) ed
FROM
(
SELECT
j.id ,
j.d sd ,
q.D ed
FROM temp j
JOIN temp q
ON
j.id = q.id
AND j.d <= q.d
GROUP BY j.id ,
j.d ,
q.d
) a
WHERE datediff(m,a.sd,a.ed)<=2
GROUP BY a.id ,
a.sd
)x
GROUP BY x.id ,
x.ed
ORDER BY x.id ,
min(x.sd) ,
x.ed
Upvotes: 1