Answered by:
Calculate start and end dates

Question
-
Hi, I have a table that has a single field that records a date. Is there a way to determine the first date in a sequence and the last date of the same sequence grouped by an ID, without using a cursor?
For example
aID aDate 1 21/01/2013 1 22/01/2013 1 23/01/2013 1 4/02/2013 2 10/02/2013 2 11/02/2013 aID StartDate EndDate 1 21/01/2013 23/01/2013 1 4/02/2013 4/02/2013 2 10/02/2013 11/02/2013
Many thanks,
Jonathan
Sunday, January 20, 2013 8:47 PM
Answers
-
This is a gaps and islands problem. More precisely, an islands problem.
This can be solved by numbering the rows with row number, and then take the distance between the value and the numbering. Within each group, the difference is constant.
;WITH CTE AS (
SELECT aID, aDate, convert(int, aDate) AS aDateNum
row_number() OVER(PARTITION aID ORDER BY aDate) AS rowno
FROM tbl
)
SELECT aID, MIN(aDate), MAX(aDate)
FROM CTE
GROUP BY aID, rowno - aDateNum
Erland Sommarskog, SQL Server MVP, esquel@sommarskog.se- Marked as answer by Kiwi Jonathan Sunday, January 20, 2013 9:14 PM
Sunday, January 20, 2013 9:04 PM
All replies
-
Looks like your grouping values by month as well.
SELECT aID, DatePart(month, aDate) AS [aMonth], MIN(aDate), MAX(aDate)
FROM tbl
GROUP BY aID, DatePart(month, aDate)Order By aID, [aMonth]
- Edited by carbonc Sunday, January 20, 2013 9:04 PM
Sunday, January 20, 2013 8:58 PM -
I appologise for giving the impression that the dates are grouped by the month. It is possible that the same ID has more than 1 lot of date sequencial dates in the same month.
Jonathan
Sunday, January 20, 2013 9:03 PM -
This is a gaps and islands problem. More precisely, an islands problem.
This can be solved by numbering the rows with row number, and then take the distance between the value and the numbering. Within each group, the difference is constant.
;WITH CTE AS (
SELECT aID, aDate, convert(int, aDate) AS aDateNum
row_number() OVER(PARTITION aID ORDER BY aDate) AS rowno
FROM tbl
)
SELECT aID, MIN(aDate), MAX(aDate)
FROM CTE
GROUP BY aID, rowno - aDateNum
Erland Sommarskog, SQL Server MVP, esquel@sommarskog.se- Marked as answer by Kiwi Jonathan Sunday, January 20, 2013 9:14 PM
Sunday, January 20, 2013 9:04 PM -
That's supurb. Really do appreciate your time and expertise.
Jonathan
Sunday, January 20, 2013 9:14 PM -
do 1 conversion to get the result.
create table #test1
(aid int, adate date)
insert into #test1 values
(1,'2013/01/21'),(1,'2013/01/22'),(1,'2013/01/23') ,(1,'2013/01/26'),(1,'2013/01/29'),(1,'2013/02/4'),(1,'2013/02/9'),(1,'2013/02/15'),(2,'2013/02/13'),(2,'2013/02/11')
select * from #test1 order by aid,adate
aid adate
1 2013-01-21
1 2013-01-22
1 2013-01-23
1 2013-01-26
1 2013-01-29
1 2013-02-04
1 2013-02-09
1 2013-02-15
2 2013-02-11
2 2013-02-13
with cte as (
SELECT aID, adate,
row_number() OVER(PARTITION by aID ORDER BY adate) AS acheck
FROM #test1
) select aid,min(adate) StartDate,max(adate) EndDate from cte
group by aid,ceiling(acheck/2.0)
order by aid,MIN(adate),MAX(adate)
aid StartDate EndDate
1 2013-01-21 2013-01-22
1 2013-01-23 2013-01-26
1 2013-01-29 2013-02-04
1 2013-02-09 2013-02-15
2 2013-02-11 2013-02-13Monday, January 21, 2013 3:28 AM -
Sorry must be missing something obvious but could you clarify your answer. I tried to code the question and your answer as follows:
USE tempdb; GO DROP TABLE tbl GO CREATE TABLE tbl ( aID INT, aDate DATE ) INSERT tbl (aID, aDate) VALUES (1, '20130121'), (1, '20130122'), (1, '20130123'), (1, '20130204'), (2, '20130210'), (2, '20130211') ;WITH CTE AS ( SELECT aID, aDate, CONVERT(INT, aDate) AS 'aDateNum', ROW_NUMBER() OVER(PARTITION BY aID ORDER BY aDate) AS 'rowno' FROM tbl ) SELECT aID, MIN(aDate), MAX(aDate) FROM CTE GROUP BY aID, rowno - aDateNum
I am getting the following error:
Msg 529, Level 16, State 2, Line 18
Explicit conversion from data type date to bigint is not allowed.I think the issue is with:
CONVERT(INT, aDate) AS 'aDateNum'
Could you please point out what I have got wrong.
Thanks
Monday, January 21, 2013 1:19 PM -
Lesson 1:
When you post this kind of question, you do yourself a service if you post:
1) CREATE TABLE statement for you table.
2) INSERT statements with sample data.
3) The desired result given the sample.
4) Which version of SQL Server you are using.This makes it possible to copy and paste into a query window to develop a tested solution.
Without it, you get what you get.
In this case, you should be able to get by a two-step conversion:
convert(int, convert(date, aDate))
An alternative is to use datediff against an anchor date to produce numbers for the dates:
datediff(day, '20000101', aDate)
Erland Sommarskog, SQL Server MVP, esquel@sommarskog.se- Proposed as answer by Naomi N Tuesday, January 22, 2013 3:08 AM
Monday, January 21, 2013 2:05 PM -
Apparently you can not convert date to int (this conversion is not allowed), but you can convert datetime to int. So, I changed the above to convert(int, convert(datetime, aDate)) and it worked.
For every expert, there is an equal and opposite expert. - Becker's Law
My blogTuesday, January 22, 2013 3:10 AM