Calculate time difference from mutiple rows using SQL Server 2008 - sql

I have 2 tables, 01 is current status and 01 is finish status.
I want to calculate time difference of 2 rows that have the same PO_NO,MANAGEMENT_NO,PROCESS_NAME .
Each PROCESS_NAME has the STATUS (Start/Finish)
ID INDEXNO PO_NO ITEM_CD MANAGEMENT_NO SEQ PROCESS_NAME STATUS Time_Occurrence TimeDiff (Minute)
43 126690 GV12762 332393961 616244 6 RFID Start 17-03-18 13:28 NULL
44 126690 GV12762 332393961 616244 6 RFID Finish 17-03-18 13:29 0
49 141646 GV14859 7E7060100 619005 2 Imprint Start 19-03-18 13:23 NULL
50 141646 GV14859 7E7060100 619005 2 Imprint Finish 19-03-18 13:30 7
48 141646 GV14859 7E7060100 619005 1 R.M.Requisition Start 19-03-18 13:18 NULL
56 141646 GV14859 7E7060100 619005 1 R.M.Requisition Finish 19-03-18 15:54 156
The expected result is : TimeDiff (Minute) column
select PO_NO, [MANAGEMENT_NO],[STATUS] [Time_Occurrence],
datediff(minute, (isnull((select [Time_Occurrence] from [TBL_FINISH_STATUS] t1 where t1.id=t2.id-1), dateadd(dd, 0, datediff(dd, 0, getdate())))), [Time_Occurrence])TimeDiff
from [PROC_MN].[dbo].[TBL_FINISH_STATUS] t2
ORDER BY PO_NO,MANAGEMENT_NO,ITEM_CD,Time_Occurrence
With above query, the result is far wrong with the expected result
Could anyone help me please?
Note: the ID column (48,56) of SEQ 1 of PO_NO: GV14859

If I understand what you want, then this seems like a simple query for it:
select INDEXNO, PO_NO, ITEM_CD, MANAGEMENT_NO, SEQ,
datediff(minute,
min(case when status = 'Start' then Time_Occurrence end),
max(case when status = 'Finish' then Time_Occurrence end)
) as timediff
from t
group by INDEXNO, PO_NO, ITEM_CD, MANAGEMENT_NO, SEQ;
Here is a SQL Fiddle.

It is not really clear what you are expecting as a result. Looking at your data sample, the design looks flawed from the start. There is too much redundancy for an SQL database. Maybe you don't have any control over the existing database. Anyway, this could be solved in N different ways and if my memory is not wrong, LEAD\LAG functions didn't exist in SQL server 2008 (but row_number is there as another solution). I tried to create something that is even compatible with older versions, but not sure if that is what you meant as a result:
DECLARE #myTable TABLE([ID] INT,
[INDEXNO] INT,
[PO_NO] VARCHAR(7),
[ITEM_CD] VARCHAR(10),
[MANAGEMENT_NO] INT,
[SEQ] INT,
[PROCESS_NAME] VARCHAR(15),
[STATUS] VARCHAR(6),
[Time_Occurrence] DATETIME,
[TimeDiff] VARCHAR(4));
INSERT INTO #myTable([ID], [INDEXNO], [PO_NO], [ITEM_CD], [MANAGEMENT_NO], [SEQ], [PROCESS_NAME], [STATUS], [Time_Occurrence], [TimeDiff])
VALUES(43, 126690, 'GV12762', '332393961', 616244, 6, 'RFID', 'Start', '20180317 13:28', NULL),
(44, 126690, 'GV12762', '332393961', 616244, 6, 'RFID', 'Finish', '20180317 13:29', '0'),
(49, 141646, 'GV14859', '7E7060100', 619005, 2, 'Imprint', 'Start', '20180319 13:23', NULL),
(50, 141646, 'GV14859', '7E7060100', 619005, 2, 'Imprint', 'Finish', '20180319 13:30', '7'),
(48, 141646, 'GV14859', '7E7060100', 619005, 1, 'R.M.Requisition', 'Start', '20180318 13:18', NULL),
(56, 141646, 'GV14859', '7E7060100', 619005, 1, 'R.M.Requisition', 'Finish', '20180318 15:54', '156');
SELECT * FROM #myTable;
WITH
Starters AS (
SELECT ID, PO_NO, [MANAGEMENT_NO], [PROCESS_NAME], [Time_Occurrence]
FROM #myTable
WHERE STATUS='Start'
),
Finishers AS (
SELECT ID, PO_NO, [MANAGEMENT_NO], [PROCESS_NAME], [Time_Occurrence]
FROM #myTable
WHERE STATUS='Finish'
)
SELECT s.PO_NO, s.MANAGEMENT_NO, s.PROCESS_NAME,
s.Time_Occurrence as [Start], f.Time_Occurrence as [End],
DATEDIFF(MINUTE, s.Time_Occurrence, f.Time_Occurrence) AS TIMEdiff
FROM Starters s
LEFT JOIN Finishers f ON s.PO_NO=f.PO_NO
AND s.MANAGEMENT_NO=f.MANAGEMENT_NO
AND f.PROCESS_NAME=s.PROCESS_NAME;

Related

Table with inconsistent starttime and stoptime

I have a table that contains a date, a starttime, and a stoptime. I have several problems that I don't know how to solve for. Each row contains either a starttime OR a stoptime (not both). While this itself is not a problem, I need to calculate the runtime by date. Also, there are instances of multiple starttimes before there is a stoptime registered. Assume the following:
Date, Starttime, Stoptime
4/1/2016, 23:00:00, NULL
4/2/2016, NULL, 03:00:00
4/2/2016, 05:00:00, NULL
4/2/2016, 07:00:00, NULL
4/2/2016, NULL, 08:00:00
4/2/2016, 10:00:00, NULL
4/2/2016, NULL, 10:15:00
I need the output to be:
4/1/2016, 01:00:00
4/2/2016, 06:15:00
I have tried a few things, with very poor results. Can any experts out there solve this problem?
Here's one way how you could handle this:
Setup:
CREATE TABLE #Table1
([Date] date, [Starttime] time, [Stoptime] time)
;
INSERT INTO #Table1
([Date], [Starttime], [Stoptime])
VALUES
('2016-04-01', '23:00:00', NULL),
('2016-04-02', NULL, '00:00:00'),
('2016-04-02', '00:00:00', NULL),
('2016-04-02', NULL, '03:00:00'),
('2016-04-02', '05:00:00', NULL),
('2016-04-02', '07:00:00', NULL),
('2016-04-02', NULL, '08:00:00'),
('2016-04-02', '10:00:00', NULL),
('2016-04-02', NULL, '10:15:00')
;
SQL:
select
convert(date, StartTime),
convert(time, dateadd(second, sum(datediff(second, StartTime, EndTime)),0))
from (
select
min(Time) as StartTime,
min(case when Type = 0 then Time end) as EndTime
from
(
select
sum(Type) over (order by Time asc, Type Asc) as GRP, *
from (
select
isnull(lag(Type) over (order by Time asc, Type Asc), -1) as LagType, *
from (
select
case when Starttime is NULL then 0 else 1 end as Type,
case when Starttime is NULL
then convert(datetime, [Date]) + convert(datetime, Stoptime)
else convert(datetime, [Date]) + convert(datetime, Starttime) end as Time
from
#Table1
) A
) B
where Type != 1 or LagType != 1
) C
group by GRP
) D
group by convert(date, StartTime)
Result:
2016-04-01 01:00:00
2016-04-02 06:15:00
This requires that you have start and stop record in your data for each of the days, so you'll need to use union or something like that to add that to your initial data.
The innermost select generates typing (0/1) for the rows and calculates the date + start / end time into a single column. The next one adds TypeLag to the data and it's used to remove the duplicate start records. The next select has a running total over Type, so that start and end records belonging together will have a unique group number. The rest will just pick the start time + earliest end time from each group and calculate the durations.

Finding the largest time overlap in T-SQL

I'm trying to do this on SQL Server 2008 R2.
I have a table with 4 columns:
parent_id INT
child_id INT
start_time TIME
end_time TIME
You should look at the children as sub-processes that run for the parent program. All these sub-processes are run once every day, and each child run within its given time span. I want to find the largest overlap of time intervals for each parent based on the times of its children, i.e. I want to know the longest possible overlap where all the sub-processes are running. The fact that each time span is repeated every day means that even if child's time interval spans midnight (i.e. 23:00-10:00), it can overlap with a child that only runs in the morning (i.e. 07:00-09:00), because even if they don't overlap on "the first day", they will overlap on all subsequent days.
The output should look like this:
parent_id INT
start_time TIME
end_time TIME
valid BIT
Where valid = 1 if an overlap was found and valid = 0 if no overlap was found.
A couple of important pieces of information:
A time interval can span midnight, i.e. start_time = 23:00 and end_time = 03:00, which is a time interval of 4 hours.
Two time intervals may overlap in two different places, i.e. start_time1 = 13:00, end_time1 = 06:00, start_time2 = 04:00, end_time2 = 14:00. This would give the largest overlap as 04:00 - 06:00 = 2 hours.
There may be no overlap common for the children of a given parent, in which case the out put for that parent would be start_time = NULL, end_time = NULL and valid = 0.
If a child interval spans the whole day, then start_time = NULL and end_time = NULL. This was chosen to avoid having a day as 00:00-24:00, which would slice overlaps crossing midnight in two, i.e. parent 3 below would end up having two overlaps (23:00-24:00 and 00:00 - 004:00), in stead of one (23:00-04:00).
An overlap is only an overlap if the time interval is shared by all the children of a parent.
The time span of one child can never be longer than 24 hours.
Take this example:
parent_id child_id start_time end_time
1 1 06:00 14:00
1 2 13:00 09:00
1 3 07:00 09:00
2 1 12:00 17:00
2 2 09:00 11:00
3 1 NULL NULL
3 2 23:00 04:00
4 1 NULL NULL
4 2 NULL NULL
10 1 06:11 14:00
10 2 06:00 09:00
10 3 05:00 08:44
11 1 11:38 17:00
11 2 09:02 12:11
These data would produce this result set:
parent_id start_time end_time valid
1 07:00 09:00 1
2 NULL NULL 0
3 23:00 04:00 1
4 NULL NULL 1
10 06:11 08:44 1
11 11:38 12:11 1
The overlap for a parent is the time interval that is shared by all its children. So the overlap for parent 10 is found by finding the overlap where all 3 children share time:
Child 1 (06:11-14:00) and 2 (06:00-09:00) overlap from 06:11 to 09:00. This overlap time interval is then applied to child 3 (05:00-08:44), which gives an overlap of 06:11 to 08:44, since this interval is the only interval where all 3 children share common time.
I hope this makes sense.
I can do it with a cursor, but I would really prefer to avoid cursors. I have been wracking my brain about how to do it without cursors, but I have come up short. Is there any way of doing it without cursors?
EDIT: Expanded the text for clause 4, to explain the decision of having a full day be NULL to NULL, in stead of 00:00 to 00:00.
EDIT: Expanded the examples with two more cases. The new cases have parent ID 10 and 11.
EDIT: Inserted explanation of how the overlap for parent 10 is found.
EDIT: Clarified clause 3. Added clauses 5 and 6. Went into detail about what this is all about.
Based on your question, I think your output should be:
parent_id start_time end_time valid
1 07:00 09:00 1
2 NULL NULL 0
3 23:00 04:00 1
4 NULL NULL 1
10 06:11 08:44 1
11 11:38 12:11 1
And here is a set-based solution:
DECLARE #Times TABLE
(
parent_id INT
,child_id INT
,start_time TIME
,end_time TIME
);
INSERT INTO #Times
VALUES
(1, 1, '06:00', '14:00')
,(1, 2, '13:00', '09:00')
,(1, 3, '07:00', '09:00')
,(2, 1, '12:00', '17:00')
,(2, 2, '09:00', '11:00')
,(3, 1, NULL, NULL)
,(3, 2, '23:00', '04:00')
,(4, 1, NULL, NULL)
,(4, 2, NULL, NULL)
,(10, 1, '06:11', '14:00')
,(10, 2, '06:00', '09:00')
,(10, 3, '05:00', '08:44')
,(11, 1, '11:38', '17:00')
,(11, 2, '09:02', '12:11');
DECLARE #Parents TABLE
(
parent_id INT PRIMARY KEY
,ChildCount INT
)
INSERT INTO #Parents
SELECT
parent_id
,COUNT(DISTINCT child_id) AS ChildCount
FROM
#Times
GROUP BY
parent_id
DECLARE #StartTime DATETIME2 = '00:00'
DECLARE #MinutesInTwoDays INT = 2880
DECLARE #Minutes TABLE(ThisMinute DATETIME2 PRIMARY KEY);
WITH
MinutesCTE AS
(
SELECT
1 AS MinuteNumber
,#StartTime AS ThisMinute
UNION ALL
SELECT
NextMinuteNumber
,NextMinute
FROM MinutesCTE
CROSS APPLY (VALUES(MinuteNumber+1,DATEADD(MINUTE,1,ThisMinute))) NextDates(NextMinuteNumber,NextMinute)
WHERE
NextMinuteNumber <= #MinutesInTwoDays
)
INSERT INTO #Minutes
SELECT ThisMinute FROM MinutesCTE M OPTION (MAXRECURSION 2880);
DECLARE #SharedMinutes TABLE
(
ThisMinute DATETIME2
,parent_id INT
,UNIQUE(ThisMinute,parent_id)
);
WITH TimesCTE AS
(
SELECT
Times.parent_id
,Times.child_id
,CAST(ISNULL(Times.start_time,'00:00') AS datetime2) AS start_time
,
DATEADD
(
DAY
,
CASE
WHEN Times.end_time IS NULL THEN 2
WHEN Times.start_time > Times.end_time THEN 1
ELSE 0
END
,CAST(ISNULL(Times.end_time,'00:00') AS datetime2)
) as end_time
FROM
#Times Times
UNION ALL
SELECT
Times.parent_id
,Times.child_id
,DATEADD(DAY,1,CAST(Times.start_time as datetime2)) AS start_time
,DATEADD(DAY,1,CAST(Times.end_time AS datetime2)) AS end_time
FROM
#Times Times
WHERE
start_time < end_time
)
--Get minutes shared by all children of each parent
INSERT INTO #SharedMinutes
SELECT
M.ThisMinute
,P.parent_id
FROM
#Minutes M
JOIN
TimesCTE T
ON
M.ThisMinute BETWEEN start_time AND end_time
JOIN
#Parents P
ON T.parent_id = P.parent_id
GROUP BY
M.ThisMinute
,P.parent_id
,P.ChildCount
HAVING
COUNT(DISTINCT T.child_id) = P.ChildCount
--get results
SELECT
parent_id
,CAST(CASE WHEN start_time = '1900-01-01' AND end_time = '1900-01-02 23:59' THEN NULL ELSE start_time END AS TIME) AS start_time
,CAST(CASE WHEN start_time = '1900-01-01' AND end_time = '1900-01-02 23:59' THEN NULL ELSE end_time END AS TIME) AS end_time
,valid
FROM
(
SELECT
P.parent_id
,MIN(ThisMinute) AS start_time
,MAX(ThisMinute) AS end_time
,CASE WHEN MAX(ThisMinute) IS NOT NULL THEN 1 ELSE 0 END AS valid
FROM
#Parents P
LEFT JOIN
#SharedMinutes SM
ON P.parent_id = SM.parent_id
GROUP BY
P.parent_id
) Results
You may find that the iterative algorithm you have outlined in your question would be more efficient. But I would use a WHILE loop instead of a cursor if you take that approach.
This might be a very verbose method of achieving the desired results, but it works for the given dataset, although it should be tested with larger data.
I've simply joined the table to itself where the parent_id matches and the child_id is different to get all of the combinations of times that might overlap and then performed some DATEDIFF's to calculate the difference, before filtering and grouping the output.
You can run the below in isolation to test and tweak if required:
-- setup initial table
CREATE TABLE #OverlapTable
(
[parent_id] INT ,
[child_id] INT ,
[start_time] TIME ,
[end_time] TIME
);
-- insert dummy data
INSERT INTO #OverlapTable
( [parent_id], [child_id], [start_time], [end_time] )
VALUES ( 1, 1, '06:00', '14:00' ),
( 1, 2, '13:00', '09:00' ),
( 1, 3, '07:00', '09:00' ),
( 2, 1, '12:00', '17:00' ),
( 2, 2, '09:00', '11:00' ),
( 3, 1, NULL, NULL ),
( 3, 2, '23:00', '04:00' ),
( 4, 1, NULL, NULL ),
( 4, 2, NULL, NULL );
-- insert all combinations into a new temp table #Results with overlap calculations
SELECT *
INTO #Results
FROM ( SELECT t1.parent_id ,
t1.start_time ,
t1.end_time ,
t2.start_time AS t2_start_time ,
t2.end_time AS t2_end_time ,
CASE WHEN t1.start_time IS NULL
AND t1.end_time IS NULL THEN 0
WHEN t1.start_time BETWEEN t2.start_time
AND t2.end_time
THEN DATEDIFF(HOUR, t1.start_time, t2.end_time)
WHEN t1.end_time BETWEEN t2.start_time AND t2.end_time
THEN DATEDIFF(HOUR, t2.start_time, t1.end_time)
ELSE NULL
END AS Overlap
FROM #OverlapTable t1
INNER JOIN #OverlapTable t2 ON t2.parent_id = t1.parent_id
AND t2.child_id != t1.child_id
) t
-- SELECT * FROM #Results -- this shows intermediate results
-- filter and group results with the largest overlaps and handle other cases
SELECT DISTINCT
r.parent_id ,
CASE WHEN r.Overlap IS NULL THEN NULL
ELSE CASE WHEN r.start_time IS NULL THEN r.t2_start_time
ELSE r.start_time
END
END start_time ,
CASE WHEN r.Overlap IS NULL THEN NULL
ELSE CASE WHEN r.end_time IS NULL THEN r.t2_end_time
ELSE r.end_time
END
END end_time ,
CASE WHEN r.Overlap IS NULL THEN 0
ELSE 1
END Valid
FROM #Results r
WHERE EXISTS ( SELECT parent_id ,
MAX(Overlap)
FROM #Results
WHERE r.parent_id = parent_id
GROUP BY parent_id
HAVING MAX(Overlap) = r.Overlap
OR ( MAX(Overlap) IS NULL
AND r.Overlap IS NULL
) )
DROP TABLE #Results
DROP TABLE #OverlapTable
Hope that helps.

Complicated grouping and time difference

So I have a log table that is putting in data like:
LogId SiteNumber Unit IDNumber LogCode EnteredDateTime ChangeMode HowEntered
----- ---------- ---- -------- ------- ---------------- ---------- -----------
851 1 16 - 0 23502 BDISCHSET 2011-11-12 11:48:08.890 Discharging Soon SERIES
866 1 16 - 0 NULL BDISCHRED 2011-11-12 21:45:11.657 Discharged SERIES
113 2 2001 - 0 12384 BDISCHSET 2011-10-28 09:27:08.773 Discharging Soon SERIES
125 2 2001 - 0 NULL BDISCHRED 2011-10-28 10:38:08.060 Discharged SERIES
119 2 2002 - 0 12394 BDISCHSET 2011-10-28 10:01:12.443 Discharging Soon SERIES
139 2 2002 - 0 NULL BDISCHRED 2011-10-28 14:01:11.120 Discharged SERIES
776 2 2002 - 0 12331 BDISCHSET 2011-11-10 09:08:09.443 Discharging Soon SERIES
783 2 2002 - 0 NULL BDISCHRED 2011-11-10 11:08:08.537 Discharged SERIES
What I need to do is group the records per person and calculate the amount of time it took to discharge.
For example:
Unit 2002 - 0 has two different people to group, it would be:
Person 12394 10/28/2011 from 10:01 to 14:01 = 4 hours to discharge
Person 12331 11/10/2011 from 9:08 to 11:08 = 2 hours to discharge
Any help would be greatly appreciated.
As others are pointing out in the comments, I don't think the design / architecture we're looking at is great. If you can guarantee that rows will ALWAYS be sequential, i.e. for every set of two rows, the first row is a Patient with change mode of Discharging Soon and the 2nd row is the same patient with a change mode of Discharged, then this should work.
;WITH OrderedTable AS
(
SELECT Unit
, IDNumber
, EnteredDateTime
, ROW_Number() OVER (Partition BY Unit ORDER BY Unit) RN
FROM #t -- YOUR TABLE NAME GOES HERE
)
SELECT t1.Unit
, t1.IDNumber
, t1.EnteredDateTime as TimeIn
, t2.EnteredDateTime as TimeOut
, DATEDIFF(hour, t1.EnteredDateTime, t2.EnteredDateTime ) TimeElapsedInHours
FROM OrderedTable t1
JOIN OrderedTable t2 ON t1.Unit = t2.Unit AND t2.RN = t1.RN + 1
WHERE t1.RN % 2 <> 0
But I want to be clear - I don't know how safe this solution is based on your actual data. However, it will give you the results you want, based on the sample data you provided.
Even if you can't use this exact approach because of your data model, I feel like there are a few things in it that you might could use to craft a solution. These will make for interesting reading if nothing else.
DATEDIFF function
Common Table Expressions
ROW_NUMBER function
Here is the test I used...
declare #t TABLE
(
LogId int,
SiteNumber int,
Unit varchar(50),
IDNumber int ,
LogCode varchar(50),
EnteredDateTime datetime,
ChangeMode varchar(50),
HowEntered varchar(50)
)
insert into #t values (851, 1, '16 - 0', 23502, 'BDISCHSET', '2011-11-12 11:48:08.890', 'Discharging Soon', 'SERIES')
insert into #t values (866, 1, '16 - 0 ', NULL, 'BDISCHRED', '2011-11-12 21:45:11.657', 'Discharged', 'SERIES')
insert into #t values (113, 2, '2001 - 0', 12384, 'BDISCHSET', '2011-10-28 09:27:08.773', 'Discharging Soon', 'SERIES')
insert into #t values (125, 2, '2001 - 0', NULL, 'BDISCHRED', '2011-10-28 10:38:08.060', 'Discharged', 'SERIES')
insert into #t values (119, 2, '2002 - 0', 12394, 'BDISCHSET', '2011-10-28 10:01:12.443', 'Discharging Soon', 'SERIES')
insert into #t values (139, 2, '2002 - 0', NULL, 'BDISCHRED', '2011-10-28 14:01:11.120', 'Discharged', 'SERIES')
insert into #t values (776, 2, '2002 - 0', 12331, 'BDISCHSET', '2011-11-10 09:08:09.443', 'Discharging Soon', 'SERIES')
insert into #t values (783, 2, '2002 - 0', NULL, 'BDISCHRED', '2011-11-10 11:08:08.537', 'Discharged', 'SERIES')
;WITH OrderedTable AS
(
SELECT Unit
, IDNumber
, EnteredDateTime
, ROW_Number() OVER (Partition BY Unit ORDER BY Unit) RN
FROM #t -- YOUR TABLE NAME GOES HERE
)
SELECT t1.Unit
, t1.IDNumber
, t1.EnteredDateTime as TimeIn
, t2.EnteredDateTime as TimeOut
, DATEDIFF(hour, t1.EnteredDateTime, t2.EnteredDateTime ) TimeElapsedInHours
FROM OrderedTable t1
JOIN OrderedTable t2 ON t1.Unit = t2.Unit AND t2.RN = t1.RN + 1
WHERE t1.RN % 2 <> 0

SQL Counting Total Time but resetting total if large gap

I have a table containing device movements.
MoveID DeviceID Start End
I want to find out if there is a way to sum up the total movement days for each device to the present. However if there is a gap 6 weeks bewtween an end date and the next start date then the time count is reset.
MoveID DeviceID Start End
1 1 2011-1-1 2011-2-1
2 1 2011-9-1 2011-9-20
3 1 2011-9-25 2011-9-28
The total for device should be 24 days as because there is a gap of greater than 6 weeks. Also I'd like to find out the number of days since the first movement in the group in this case 28 days as the latest count group started on the 2011-9-1
I thought I could do it with a stored proc and a cursor etc (which is not good) just wondered if there was anything better?
Thanks
Graeme
create table #test
(
MoveID int,
DeviceID int,
Start date,
End_time date
)
--drop table #test
insert into #test values
(1,1,'2011-1-1','2011-2-1'),
(2,1,'2011-9-1','2011-9-20'),
(3,1,'2011-9-25','2011-9-28')
select
a.DeviceID,
sum(case when datediff(dd, a.End_time, isnull(b.Start, a.end_time)) > 42 /*6 weeks = 42 days*/ then 0 else datediff(dd,a.Start, a.End_time)+1 /*we will count also the last day*/ end) as movement_days,
sum(case when datediff(dd, a.End_time, isnull(b.Start, a.end_time)) > 42 /6 weeks = 42 days/ then 0 else datediff(dd,a.Start, a.End_time)+1 /we will count also the last day/ end + case when b.MoveID is null then datediff(dd, a.Start, a.End_time) + 1 else 0 end) as total_days
from
#test a
left join #test b
on a.DeviceID = b.DeviceID
and a.MoveID + 1 = b.MoveID
group by
a.DeviceID
Let me know if you need some explanation - there can be more ways to do that...
DECLARE #Times TABLE
(
MoveID INT,
DeviceID INT,
Start DATETIME,
[End] DATETIME
)
INSERT INTO #Times VALUES (1, 1, '1/1/2011', '2/1/2011')
INSERT INTO #Times VALUES (2, 1, '9/1/2011', '9/20/2011')
INSERT INTO #Times VALUES (3, 1, '9/25/2011', '9/28/2011')
INSERT INTO #Times VALUES (4, 2, '1/1/2011', '2/1/2011')
INSERT INTO #Times VALUES (5, 2, '3/1/2011', '4/20/2011')
INSERT INTO #Times VALUES (6, 2, '5/1/2011', '6/20/2011')
DECLARE #MaxGapInWeeks INT
SET #MaxGapInWeeks = 6
SELECT
validTimes.DeviceID,
SUM(DATEDIFF(DAY, validTimes.Start, validTimes.[End]) + 1) AS TotalDays,
DATEDIFF(DAY, MIN(validTimes.Start), MAX(validTimes.[End])) + 1 AS TotalDaysInGroup
FROM
#Times validTimes LEFT JOIN
#Times timeGap
ON timeGap.DeviceID = validTimes.DeviceID
AND timeGap.MoveID <> validTimes.MoveID
AND DATEDIFF(WEEK, validTimes.[End], timeGap.Start) > #MaxGapInWeeks
WHERE timeGap.MoveID IS NULL
GROUP BY validTimes.DeviceID

How to subtract two data entries in SQL Server

In my database, there is one table containing (ID, Name, Time, Type)
ID Name Time Type
1 Osama 12:15 AM IN
2 Osama 12:20 AM OUT
3 Osama 14:15 AM IN
4 Osama 14:20 AM OUT
I need to construct a query to output the time-difference (OUT-IN)
Name, OUT-IN
Example:
Osama, 5
Osama, 5
The TestData CTE here is purely for testing purposes. Also, I note that there is an error your data. Last I checked 14:15 AM isn't a valid time. It is either 14:15 (via 24 hr clock) or 2:15 AM (via 12 hr clock). Also, this solution would require SQL Server 2005 or later.
With TestData As
(
Select 1 As Id, 'Osama' As Name, '12:15' As Time, 'IN' As Type
Union All Select 2, 'Osama', '12:20', 'OUT'
Union All Select 3, 'Osama', '14:15', 'IN'
Union All Select 4, 'Osama', '14:20', 'OUT'
)
, CheckInCheckOut As
(
Select Id, Name, Time, Type
, Row_Number() Over ( Partition By Name, Type Order By Time ) As Num
From TestData
)
Select C1.Name
, DateDiff( mi, CAST(C1.Time as datetime), Cast(C2.Time As datetime) ) As [OUT-IN]
From CheckInCheckOut As C1
Join CheckInCheckOut As C2
On C2.Name = C1.Name
And C2.Type = 'OUT'
And C2.Num = C1.Num
Where C1.Type = 'IN'
You can do Max(TimeRecorded) if you are ok with assuming your times are sequential. This assumes your Ids are sequential. You can control either of those with check constraints.
declare #test table (
Id int, Name varchar(50), TimeRecorded time, TypeOfTimeRecording varchar(3)
)
insert into #test values (1, 'Osama', CONVERT(time, '12:15'), 'IN')
insert into #test values (2, 'Osama', CONVERT(time, '12:20'), 'OUT')
insert into #test values (3, 'Osama', CONVERT(time, '12:25'), 'IN')
insert into #test values (4, 'Osama', CONVERT(time, '12:30'), 'OUT')
select testOut.Name
,testOut.TimeRecorded
,testIn.TimeRecorded
,DATEDIFF(minute, testIn.TimeRecorded, testOut.TimeRecorded) as [Out - In]
from #test testOut
inner join
#test testIn on testIn.Id = (select MAX(Id) from #test where Name = testOut.Name and Id < testOut.Id and TypeOfTimeRecording = 'IN')
where testOut.TypeOfTimeRecording = 'OUT'