Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2008 Forums
 Transact-SQL (2008)
 How to find missing time records [solved]

Author  Topic 

rokhri
Starting Member

10 Posts

Posted - 2011-04-28 : 05:57:40
I have following two tables. I want to search all records from Table1 where datetime is not available for Table2 records
CREATE TABLE #Table1 (StartDate DATETIME, EndDate DATETIME)

INSERT INTO #Table1
SELECT '2011/03/01 08:01','2011/03/01 11:00'
UNION ALL
SELECT '2011/03/01 13:01','2011/03/01 16:00'
UNION ALL
SELECT '2011/03/01 18:01','2011/03/01 23:59'

CREATE TABLE #Table2 (TStartDate DATETIME, TEndDate DATETIME)
INSERT INTO #Table2
SELECT '2011/03/01 06:00', '2011/03/01 11:00'
UNION ALL
SELECT '2011/03/01 19:00',' 2011/03/01 22:00'
UNION ALL
SELECT '2011/03/01 10:00','2011/03/01 13:30'

--REQUIRED OUTPUT
StartDate,EndDate
'2011/03/01 06:00', '2011/03/01 08:00'
'2011/03/01 11:01', '2011/03/01 13:00'

nigelrivett
Master Smack Fu Yak Hacker

3385 Posts

Posted - 2011-04-28 : 06:29:34
I can see why the first row is there - tyhe first row in t2 is not covered by the t1 row.
I don't understand the second though - it seems to be from the end of the first row in t1 to the start of the 2nd but there is no coresponding entry in t2.


==========================================
Cursors are useful if you don't know sql.
SSIS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

rokhri
Starting Member

10 Posts

Posted - 2011-04-28 : 06:33:36
second row is also not convered by t1 from 11:01 to 13:00
Go to Top of Page

Seventhnight
Master Smack Fu Yak Hacker

2878 Posts

Posted - 2011-04-28 : 06:35:20
quote:
Originally posted by rokhri

second row is also not convered by t1 from 11:01 to 13:00



Right... but its not part of Table1 either...

Corey

I Has Returned!!
Go to Top of Page

nigelrivett
Master Smack Fu Yak Hacker

3385 Posts

Posted - 2011-04-28 : 06:39:59
It's not within the start and end dates of either table.

==========================================
Cursors are useful if you don't know sql.
SSIS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

rokhri
Starting Member

10 Posts

Posted - 2011-04-28 : 06:42:08
yeah both required output rows are not part of table1.
1- for first record of table2, only partial time is missing that is 6:00 to 8:00
2- for second row of table2 time is from 19:00 to 22:00 which is already being covered by table1 so NOT a part of output
3- for third row of table 2 there is no record or records in table1 which cover from 11:01 to 13:00, though from 10:00 to 11:00 it is being covered by first row of t1 and from 3:00 to 3:30 covered by by second row of t1 but time 11:01 to 13:00 is missing
i need that un-overlapping time
Go to Top of Page

nigelrivett
Master Smack Fu Yak Hacker

3385 Posts

Posted - 2011-04-28 : 06:51:54
Sorry - thought your data was in order - didn't realise it was overlapping.

==========================================
Cursors are useful if you don't know sql.
SSIS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

rokhri
Starting Member

10 Posts

Posted - 2011-04-28 : 07:06:20
any idea ???
Go to Top of Page

nigelrivett
Master Smack Fu Yak Hacker

3385 Posts

Posted - 2011-04-28 : 07:09:08
It's probably not easy.
My approach would be to first merge the rows into continuous time periods - which would be easier with a loop then find the missing bits.
Another option would be to convert to minute intervals for both tables then find the consecutive periods that are missing - that would probably be the easiest to code.

==========================================
Cursors are useful if you don't know sql.
SSIS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

rokhri
Starting Member

10 Posts

Posted - 2011-04-28 : 07:12:45
loop is a poor approach and it will slow down my query at production. I want somthing like following
http://stackoverflow.com/questions/2186730/tsql-datetime-overlapping-and-interval-values
Go to Top of Page

Seventhnight
Master Smack Fu Yak Hacker

2878 Posts

Posted - 2011-04-28 : 07:28:57
The records in the second table overlapping seem to cause some confusion...
but I suppose they could be collapsed, if I was less lazy.



Declare @t1 Table (
tStart datetime,
tEnd datetime
)

INSERT INTO @t1
SELECT '2011/03/01 08:01','2011/03/01 11:00'
UNION ALL
SELECT '2011/03/01 13:01','2011/03/01 16:00'
UNION ALL
SELECT '2011/03/01 18:01','2011/03/01 23:59'

Declare @t2 Table (
tStart datetime,
tEnd datetime
)
/*
INSERT INTO @t2
SELECT '2011/03/01 06:00', '2011/03/01 11:00'
UNION ALL
SELECT '2011/03/01 19:00',' 2011/03/01 22:00'
UNION ALL
SELECT '2011/03/01 10:00','2011/03/01 13:30' -- Overlaps with 6-11 record??
*/
INSERT INTO @t2
SELECT '2011/03/01 06:00', '2011/03/01 10:00'
UNION ALL
SELECT '2011/03/01 19:00',' 2011/03/01 22:00'
UNION ALL
SELECT '2011/03/01 10:00','2011/03/01 13:30'


;with z
As (
Select
dy = dateadd(dy,datediff(dy,0,tStart),0),
tStart,
tEnd,
r = Row_Number() Over(Partition By dateadd(dy,datediff(dy,0,tStart),0) Order By tStart)
From @t1 A
), y
As (
Select
*,
r = Row_Number() Over(Partition By dateadd(dy,datediff(dy,0,tStart),0) Order By tStart)
From
(
Select
dy = isnull(A.dy,B.dy),
tStart = isnull(A.tStart,isnull(A.dy,B.dy)),
tEnd = isnull(A.tEnd,B.tStart),
InT1 = case when A.tStart is not null then 1 else 0 end
From z A
Full Join z B
On A.r = B.r-1

Union All

Select
dy = A.dy,
tStart = A.tEnd,
tEnd = isnull(B.tStart,dateadd(minute,-1,A.dy+1)),
InT1 = 0
From z A
Left Join z B
On A.r = B.r-1
) C
Where tStart <> tEnd
), x
As (
Select
dy = dateadd(dy,datediff(dy,0,tStart),0),
tStart,
tEnd,
r = Row_Number() Over(Partition By dateadd(dy,datediff(dy,0,tStart),0) Order By tStart)
From @t2 A
), w
As (
Select
*,
r = Row_Number() Over(Partition By dateadd(dy,datediff(dy,0,tStart),0) Order By tStart)
From
(
Select
dy = isnull(A.dy,B.dy),
tStart = isnull(A.tStart,isnull(A.dy,B.dy)),
tEnd = isnull(A.tEnd,B.tStart),
InT2 = case when A.tStart is not null then 1 else 0 end
From x A
Full Join x B
On A.r = B.r-1

Union All

Select
dy = A.dy,
tStart = A.tEnd,
tEnd = isnull(B.tStart,dateadd(minute,-1,A.dy+1)),
InT2 = 0
From x A
Left Join x B
On A.r = B.r-1
) C
Where tStart <> tEnd
), cte
As (
Select
a.dy,
--C.n,
tStart = case C.n
when 1 then
case when A.tStart < B.tStart then A.tStart else B.tStart end
when 2 then
case when A.tStart < B.tStart then B.tStart else A.tStart end
when 3 then
case when A.tEnd < B.tEnd then A.tEnd else B.tEnd end
end,
tEnd =
case C.n
when 1 then
case when A.tStart < B.tStart then B.tStart else A.tStart end
when 2 then
case when A.tEnd < B.tEnd then A.tEnd else B.tEnd end
when 3 then
case when A.tEnd < B.tEnd then B.tEnd else A.tEnd end
end,
InT1 = case C.n
when 1 then
case when A.tStart < B.tStart then A.InT1 else 0 end
when 2 then A.InT1
when 3 then
case when A.tStart < B.tStart then 0 else A.InT1 end
end,
InT2 = case C.n
when 1 then
case when A.tStart < B.tStart then 0 else B.InT2 end
when 2 then B.InT2
when 3 then
case when A.tStart < B.tStart then B.InT2 else 0 end
end,

tStart1 = A.tStart,
tEnd1 = A.tEnd,
T1 = A.InT1,
tStart2 = B.tStart,
tEnd2 = B.tEnd,
T2 = B.InT2,
r = Row_Number() Over (Partition By A.dy Order By A.r, B.r, C.n)
From y A
Left Join w B
On A.tStart <= B.tEnd
and A.tEnd > B.tStart
Cross Join (Select n=1 Union Select n=2 Union Select n=3) C
)

Select
dy,
tStart,
tEnd,
InT1 = max(InT1),
InT2 = max(InT2)
From cte
Where tStart <> tEnd
group by dy, tStart, tEnd
Having max(InT2) = 0
Order By 1, 2


Corey

I Has Returned!!
Go to Top of Page

nigelrivett
Master Smack Fu Yak Hacker

3385 Posts

Posted - 2011-04-28 : 07:30:36
maybe

;with cte21 as (select startdate = MIN(TStartDate), enddate = MAX(TEndDate) from #Table2)
, cte22 as
(select tme = startdate from cte21
union all
select DATEADD(mi,1,tme) from cte22 where tme < (select enddate from cte21)
)
,cte23 as (select * from cte22 where exists (select * from #Table2 where tme between TStartDate and TEndDate))
,cte as (select tme from cte23 where not exists (select * from #Table1 where tme between StartDate and EndDate)) -- gaps
,ctea as (select *, seq = ROW_NUMBER() over (order by tme) from cte c1 where not exists (select * from cte c2 where DATEADD(mi,1,c2.tme) = c1.tme))
,cteb as (select *, seq = ROW_NUMBER() over (order by tme) from cte c1 where not exists (select * from cte c2 where DATEADD(mi,1,c1.tme) = c2.tme))
select startdate = ctea.tme, enddate = cteb.tme from ctea
join cteb on ctea.seq = cteb.seq
option (maxrecursion 0)


==========================================
Cursors are useful if you don't know sql.
SSIS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

Seventhnight
Master Smack Fu Yak Hacker

2878 Posts

Posted - 2011-04-28 : 07:32:59
This is much shorter...

I didn't test it though

quote:
Originally posted by nigelrivett

maybe

;with cte21 as (select startdate = MIN(TStartDate), enddate = MAX(TEndDate) from #Table2)
, cte22 as
(select tme = startdate from cte21
union all
select DATEADD(mi,1,tme) from cte22 where tme < (select enddate from cte21)
)
,cte23 as (select * from cte22 where exists (select * from #Table2 where tme between TStartDate and TEndDate))
,cte as (select tme from cte23 where not exists (select * from #Table1 where tme between StartDate and EndDate)) -- gaps
,ctea as (select *, seq = ROW_NUMBER() over (order by tme) from cte c1 where not exists (select * from cte c2 where DATEADD(mi,1,c2.tme) = c1.tme))
,cteb as (select *, seq = ROW_NUMBER() over (order by tme) from cte c1 where not exists (select * from cte c2 where DATEADD(mi,1,c1.tme) = c2.tme))
select startdate = ctea.tme, enddate = cteb.tme from ctea
join cteb on ctea.seq = cteb.seq
option (maxrecursion 0)


==========================================
Cursors are useful if you don't know sql.
SSIS can be used in a similar way.
Beer is not cold and it isn't fizzy.



Corey

I Has Returned!!
Go to Top of Page

nigelrivett
Master Smack Fu Yak Hacker

3385 Posts

Posted - 2011-04-28 : 07:36:21
But it transforms into minute intervals. If the time intervals are large I would consider using temp tables so that it could be indexed. Doing this sort of thing can be surprisingly quick but you have to be careful with it.

People tend to find this easier to understand rather than having to deal with starts and ends of periods and the various conditions that can occur.

==========================================
Cursors are useful if you don't know sql.
SSIS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

rokhri
Starting Member

10 Posts

Posted - 2011-04-28 : 07:40:34
wow....nigelrivett
Thanx buddy

thanx Seventhnight

(Thats the reason i left ORACLE, SQL Server community is much more helpful)
Go to Top of Page

rokhri
Starting Member

10 Posts

Posted - 2011-04-28 : 07:42:40
nigelrivett
i need ur permission to publish this solution through my blog
Go to Top of Page

Seventhnight
Master Smack Fu Yak Hacker

2878 Posts

Posted - 2011-04-28 : 07:45:48
Yeah... it will take a little dissecting for me to wrap my head around this, but hey, that's the whole point

Corey

I Has Returned!!
Go to Top of Page

nigelrivett
Master Smack Fu Yak Hacker

3385 Posts

Posted - 2011-04-28 : 07:52:20
quote:
Originally posted by rokhri

nigelrivett
i need ur permission to publish this solution through my blog



Go ahead - mention me as I may turn this into an article at some point together with the other dozen+ that are waiting to be sent for publishing.

==========================================
Cursors are useful if you don't know sql.
SSIS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

nigelrivett
Master Smack Fu Yak Hacker

3385 Posts

Posted - 2011-04-28 : 08:03:34
Here's a version with a few coments

-- limits of period - first start date, last end date
;with cte21 as (select startdate = MIN(TStartDate), enddate = MAX(TEndDate) from #Table2)
-- table of minutes bewteen start and end dates
, cte22 as
(select tme = startdate from cte21
union all
select DATEADD(mi,1,tme) from cte22 where tme < (select enddate from cte21)
)
-- minutes that are included in #Table2 periods
,cte23 as (select * from cte22 where exists (select * from #Table2 where tme between TStartDate and TEndDate))
-- minutes that are not covered by #Table1 periods
,cte as (select tme from cte23 where not exists (select * from #Table1 where tme between StartDate and EndDate)) -- gaps
-- Start dates of periods not covered
,ctea as (select *, seq = ROW_NUMBER() over (order by tme) from cte c1 where not exists (select * from cte c2 where DATEADD(mi,1,c2.tme) = c1.tme))
-- end dates of periods not covered
,cteb as (select *, seq = ROW_NUMBER() over (order by tme) from cte c1 where not exists (select * from cte c2 where DATEADD(mi,1,c1.tme) = c2.tme))
-- start and end dates of periods not covered
select startdate = ctea.tme, enddate = cteb.tme from ctea
join cteb on ctea.seq = cteb.seq
option (maxrecursion 0)


==========================================
Cursors are useful if you don't know sql.
SSIS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

Seventhnight
Master Smack Fu Yak Hacker

2878 Posts

Posted - 2011-04-28 : 08:25:44
Thanks! Saves me some trouble

quote:
Originally posted by nigelrivett

Here's a version with a few coments
...



Corey

I Has Returned!!
Go to Top of Page
   

- Advertisement -