fetch two distinct rows with discontinued dates - sql

I want to fetch two rows with discontinued dates from a data sample ex: end date of 1st row should be equal to start date of next row and I want to print whole two rows
tried lead but it did not work
select t1.*
from (select t.*, lead(cast(startdate as date)) over (order by currenykey,cast(enddate as date)) as next_start_date
from table t
) t1
where enddate <> next_start_date
start date end date
1 11/6/17 0:00.00 11/13/17 0:00.00
2 11/13/17 0:00.00 12/26/17 0:00.00
3 12/26/17 0:00.00 1/8/18 0:00.00
4 10/22/18 0:11.13 2/25/19 0:16.35
5 2/25/19 0:16.35 3/4/19 0:09.57
6 3/4/19 0:09.57 3/11/19 0:12.30
7 3/11/19 0:12.30 3/18/19 0:10.21
8 3/18/19 0:10.21 3/25/19 0:09.20
9 3/25/19 0:09.20 4/1/19 0:10.19
I want o print entire rows 3 and 4

If you're on SQL Server 2012 or later you could use the LAG and LEAD functions:
LAG (Transact-SQL)
LEAD (Transact-SQL)
For example...
declare #StackOverflow table (
[ID] int not null,
[StartDate] datetime not null,
[EndDate] datetime not null
);
insert #StackOverflow values
(1, '11/6/17 0:00.00', '11/13/17 0:00.00'),
(2, '11/13/17 0:00.00', '12/26/17 0:00.00'),
(3, '12/26/17 0:00.00', '1/8/18 0:00.00'),
(4, '10/22/18 0:11.13', '2/25/19 0:16.35'),
(5, '2/25/19 0:16.35', '3/4/19 0:09.57'),
(6, '3/4/19 0:09.57', '3/11/19 0:12.30'),
(7, '3/11/19 0:12.30', '3/18/19 0:10.21'),
(8, '3/18/19 0:10.21', '3/25/19 0:09.20'),
(9, '3/25/19 0:09.20', '4/1/19 0:10.19');
select [ID], [StartDate], [EndDate]
from (
select [ID],
[StartDate],
[EndDate],
[Previous] = cast(lag([EndDate]) over (order by [ID]) as date),
[Next] = cast(lead([StartDate]) over (order by [ID]) as date)
from #StackOverflow SO
) SO
where Previous != cast([StartDate] as date)
or Next != cast([EndDate] as date);
Which yields:
ID StartDate EndDate
3 26/12/2017 00:00:00 08/01/2018 00:00:00
4 22/10/2018 00:11:00 25/02/2019 00:16:00

Your query is on the right path, with two caveats:
You want to convert to dates for the comparison.
You need to compare both lead() and lag().
So:
select t.*
from (select t.*,
lead(startdate) over (order by startdate) as next_startdate,
lag(enddate) over (order by startdate) as prev_enddate
from t
) t
where convert(date, enddate) <> convert(date, next_startdate) or
convert(date, startdate) <> convert(date, prev_enddate) ;
That said, I think you are safer with not exists subqueries:
select *
from t
where (not exists (select 1
from t t2
where convert(date, t.startdate) = convert(date, t2.enddate)
) or
not exists (select 1
from t t2
where convert(date, t.enddate) = convert(date, t2.startdate)
)
) and
t.startdate <> (select min(t2.startdate) from t t2) and
t.startdate <> (select max(t2.startdate) from t t2) ;
Here is a db<>fiddle.
To understand why, consider what happens if the start date of line 3 changes. Here is an example where the two do not produce the same results.

Related

Calculating average by using the previous row's value and following row's value

I have calculated average values for each month. Some months are NULL and my manager wants me to use the previous row's value and following month's value and fill the months which are having NULL values.
Current result (see below pic):
Expected Result
DECLARE #DATE DATE = '2017-01-01';
WITH DATEDIM AS
(
SELECT DISTINCT DTM.FirstDayOfMonth
FROM DATEDIM DTM
WHERE Date >= '01/01/2017'
AND Date <= DATEADD(mm,-1,Getdate())
),
Tab1 AS
(
SELECT
T1.FirstDayOfMonth AS MONTH_START,
AVG1,
ROW_NUMBER() OVER (
ORDER BY DATEADD(MM,DATEDIFF(MM, 0, T1.FirstDayOfMonth),0) DESC
) AS RNK
FROM DATEDIM T1
LEFT OUTER JOIN (
SELECT
DATEADD(MM,DATEDIFF(MM, 0, StartDate),0) MONTH_START,
AVG(CAST(DATEDIFF(dd, StartDate, EndDate) AS FLOAT)) AS AVG1
FROM DATATable
WHERE EndDate >= StartDate
AND StartDate >= #DATE
AND EndDate >= #DATE
GROUP BY DATEADD(MM,DATEDIFF(MM, 0, StartDate),0)
) T2 ON T1.FirstDayOfMonth = T2.MONTH_START
)
SELECT *
FROM Tab1
Using your CTEs
select MONTH_START,
case when AVG1 is null then
(select top(1) t2.AVG1
from Tab1 t2
where t1.RNK > t2.RNK and t2.AVG1 is not null
order by t2.RNK desc)
else AVG1 end AVG1,
RNK
from Tab1 t1
Edit
Version for an average of nearest peceding and nearest following non-nulls. Both must exist otherwise NULL is returned.
select MONTH_START,
case when AVG1 is null then
( (select top(1) t2.AVG1
from Tab1 t2
where t1.RNK > t2.RNK and t2.AVG1 is not null
order by t2.RNK desc)
+(select top(1) t2.AVG1
from Tab1 t2
where t1.RNK < t2.RNK and t2.AVG1 is not null
order by t2.RNK)
) / 2
else AVG1 end AVG1,
RNK
from Tab1 t1
I can't quite tell what you are trying to calculate the average of, but this is quite simple with window functions:
select t.*,
avg(val) over (order by month_start rows between 1 preceding and 1 rollowing)
from t;
In your case, I think this translates as:
select datefromparts(year(startdate), month(startdate), 1) as float,
avg(val) as monthaverage,
avg(avg(val)) over (order by min(startdate) rows between 1 preceding and 1 following)
from datatable d
where . . .
group by datefromparts(year(startdate), month(startdate), 1)
You can manipulate previous and following row values using window functions:
SELECT MAX(row_value) OVER(
ORDER BY ... ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS Previous_Value,
MAX(row_value) OVER(
ORDER BY ... ROWS BETWEEN 1 FOLLOWING AND 1 FOLLOWING) AS Next_Value
Alternatively you can use LAG/LEAD functions and modify your sub-query where you get the AVG:
SELECT
src.MONTH_START,
CASE
WHEN src.prev_val IS NULL OR src.next_val IS NULL
THEN COALESCE(src.prev_val, src.next_val) -- Return non-NULL value (if exists)
ELSE (src.prev_val + src.next_val ) / 2
END AS AVG_new
FROM (
SELECT
DATEADD(MM,DATEDIFF(MM, 0, StartDate),0) MONTH_START,
LEAD(CAST(DATEDIFF(dd, StartDate, EndDate) AS FLOAT)) OVER(ORDER BY ...) AS prev_val,
LAG(CAST(DATEDIFF(dd, StartDate, EndDate) AS FLOAT)) OVER(ORDER BY ...) AS next_val
-- AVG(CAST(DATEDIFF(dd, StartDate, EndDate) AS FLOAT)) AS AVG1
FROM DATATable
WHERE EndDate >= StartDate
AND StartDate >= #DATE
AND EndDate >= #DATE
GROUP BY DATEADD(MM,DATEDIFF(MM, 0, StartDate),0)
) AS src
I haven't tested it, but give it a shot and see how it works. You may need to put at least one column in the ORDER BY portion of the window function.
You could try this query (I just reflected in my sample data relevant parts, I omitted date column):
declare #tbl table (rank int, value int);
insert into #tbl values
(1, null),
(2, 20),
(3, 30),
(4, null),
(5, null),
(6, null),
(7, 40),
(8, null),
(9, null),
(10, 36),
(11, 22);
;with cte as (
select *,
DENSE_RANK() over (order by case when value is null then rank else value end) drank,
case when value is null then lag(value) over (order by rank) end lag,
case when value is null then lead(value) over (order by rank) end lead
from #tbl
)
select rank, value, case when value is null then
max(lag) over (partition by grp) / 2 +
max(lead) over (partition by grp) / 2
else value end valueWithAvg
from (
select *,
rank - drank grp from cte
) a order by rank

Insert missing dates into existing table

I have a query that finds missing dates from a table.
The query is:
;WITH NullGaps AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY ChannelName, ReadingDate) AS ID,
SerialNumber, ReadingDate, ChannelName, uid
FROM
[UriData]
)
SELECT
(DATEDIFF(MINUTE, g1.ReadingDate , g2.ReadingDate) / 15) -1 AS 'MissingCount',
g1.ReadingDate AS 'FromDate', g2.ReadingDate AS 'ToDate'
FROM
NullGaps g1
INNER JOIN
NullGaps g2 ON g1.ID = (g2.ID - 1)
WHERE
DATEADD(MINUTE, 15, g1.ReadingDate) < g2.ReadingDate
The output is:
--------------------------------------------------------------
| MissingCount | FromDate | ToDate |
--------------------------------------------------------------
| 2 | 2018-09-20 14:30:00 | 2018-09-20 15:15:00 |
| 1 | 2018-09-20 15:30:00 | 2018-09-20 16:00:00 |
| 1 | 2018-09-20 20:30:00 | 2018-09-20 21:00:00 |
--------------------------------------------------------------
The output is the number of datetimes that are missing from the FromDate to the ToDate (which both exist). For example, in the first row of the output (above), the times I want to create and insert will be '2018-09-20 14:45:00' and '2018-09-20 15:00:00' (they are all 15-minute intervals)
I need to understand, how I now create the new dates and insert them into an existing table. I can create one date, but I can't create dates where there are multiple missing values between two times.
TIA
SQL Fiddle
If you also want to find the missing datetimes at the start and the end of a date?
Then comparing to generated datetimes should be a valiable method.
Such dates can be generated via a Recursive CTE.
Then you can join your data to the Recursive CTE and select those that are missing.
Or use a NOT EXISTS.
For example:
WITH RCTE AS
(
select [SerialNumber], [ChannelName], 0 as Lvl, cast(cast([ReadingDate] as date) as datetime) as ReadingDate
from [UriData]
group by SerialNumber, [ChannelName], cast([ReadingDate] as date)
union all
select [SerialNumber], [ChannelName], Lvl + 1, DATEADD(MINUTE,15,[ReadingDate])
from RCTE
where cast([ReadingDate] as date) = cast(DATEADD(MINUTE,15,[ReadingDate]) as date)
)
SELECT [SerialNumber], [ChannelName], [ReadingDate] AS FromDate
FROM RCTE r
WHERE NOT EXISTS
(
select 1
from [UriData] t
where t.[SerialNumber] = r.[SerialNumber]
and t.[ChannelName] = r.[ChannelName]
and t.[ReadingDate] = r.[ReadingDate]
);
A test can be found here
And here's another query that takes a different approuch :
WITH CTE AS
(
SELECT SerialNumber, ChannelName, ReadingDate,
LAG(ReadingDate) OVER (PARTITION BY SerialNumber, ChannelName ORDER BY ReadingDate) AS prevReadingDate
FROM [UriData]
)
, RCTE AS
(
select SerialNumber, ChannelName, 0 as Lvl,
prevReadingDate AS ReadingDate,
prevReadingDate AS MinReadingDate,
ReadingDate AS MaxReadingDate
from CTE
where DATEDIFF(MINUTE, prevReadingDate, ReadingDate) > 15
union all
select SerialNumber, ChannelName, Lvl + 1,
DATEADD(MINUTE,15,ReadingDate),
MinReadingDate,
MaxReadingDate
from RCTE
where ReadingDate < DATEADD(MINUTE,-15,MaxReadingDate)
)
select SerialNumber, ChannelName,
ReadingDate AS FromDate,
DATEADD(MINUTE,15,ReadingDate) AS ToDate,
dense_rank() over (partition by SerialNumber, ChannelName order by MinReadingDate) as GapRank,
(DATEDIFF(MINUTE, MinReadingDate, MaxReadingDate) / 15) AS TotalMissingQuarterGaps
from RCTE
where Lvl > 0 AND MinReadingDate < MaxReadingDate
ORDER BY SerialNumber, ChannelName, MinReadingDate;
You can test that one here
I don't understand your query for calculating missing values. Your question doesn't have sample data or explain the logic. I'm pretty sure that lag() would be much simpler.
But given your query (or any other), one method to expand out the data is to use a recursive CTE:
with missing as (<your query here>)
cte as (
select dateadd(minute, 15, fromdate) as dte, missingcount - 1 as missingcount
from missing
union all
select dateadd(minute, 15, dte), missingcount - 1
from cte
where missingcount > 0
)
select *
from cte;
If you have more than 100 missing times in one row, then add option (maxrecursion 0) to the end of the query.
Based on the information shared with me, I did the following which does what I need.
The first part is to find the date ranges that are missing by finding the from and to dates that have missing dates between them, then insert them into a table for auditing, but it will hold the missing dates I am looking for:
;WITH NullGaps AS(
SELECT ROW_NUMBER() OVER (ORDER BY ChannelName, ReadingDate) AS ID,SerialNumber, ReadingDate, ChannelName, uid
FROM [Staging].[UriData]
)
INSERT INTO [Staging].[MissingDates]
SELECT (DATEDIFF(MINUTE, g1.ReadingDate , g2.ReadingDate) / 15) -1 AS 'MissingCount',
g1.ChannelName,
g1.SerialNumber,
g1.ReadingDate AS FromDate,
g2.ReadingDate AS ToDate
FROM NullGaps g1
INNER JOIN NullGaps g2
ON g1.ID = (g2.ID - 1)
WHERE DATEADD(MINUTE, 15, g1.ReadingDate) < g2.ReadingDate
AND g1.ChannelName IN (SELECT ChannelName FROM staging.ActiveChannels)
AND NOT EXISTS(
SELECT 1 FROM [Staging].[MissingDates] m
WHERE m.Channel = g1.ChannelName
AND m.Serial = g1.SerialNumber
AND m.FromDate = g1.ReadingDate
AND m.ToDate = g2.ReadingDate
)
Now that I have the ranges to look for, I can now create the missing dates and insert them into the table that holds real data.
;WITH MissingDateTime AS(
SELECT DATEADD(MINUTE, 15, FromDate) AS dte, MissingCount -1 AS MissingCount, Serial, Channel
FROM [Staging].[MissingDates]
UNION ALL
SELECT DATEADD(MINUTE, 15, dte), MissingCount - 1, Serial, Channel
FROM MissingDateTime
WHERE MissingCount > 0
) -- END CTE
INSERT INTO [Staging].[UriData]
SELECT NEWID(), Serial, Channel, '999', '0', dte, CURRENT_TIMESTAMP, 0,1,0 FROM MissingDateTime m
WHERE NOT EXISTS(
SELECT 1 FROM [Staging].[UriData] u
WHERE u.ChannelName = m.Channel
AND u.SerialNumber = m.Serial
AND u.ReadingDate = m.dte
) -- END SELECT
I am sure you can offer improvements to this. This solution finds only the missing dates and allows me to back fill my data table with only the missing dates. I can also change the intervals later should other devices need to be used for different intervals. I have put the queries in two sperarate SPROC's so I can control both apects, being: one for auditing and one for back filling.

SQL Server: query to get the data between two values from same columns and calculate time difference

I have a requirement to get the number of hours between two values, say 20 and 25 or above (this will be user input values and not fixed). Below is the table with sample data.
Consider in the table on 01-09-2016 08:40 value_ID is 25 and it reaches back to 20 on 02-09-2016 13:20, I need to consider the number of hours between these two range ie 12 hours and 40 min it is .. Similarly 04-09-2016 13:20 it reached 26.3 (which is above 25 ) and '06-09-2016 16:20' reached 19.3 (below 20) and number of hours is 45 hours. I tried creating a function, however it's not working..
CODE TO CREATE TABLE:
CREATE TABLE [dbo].[NumOfHrs](
[ID] [float] NULL,
[Date] [datetime] NULL,
[Value_ID] [float] NULL
) ON [PRIMARY]
CODE to insert data :
INSERT INTO [dbo].[NumOfHrs]
([ID]
,[Date]
,[Value_ID])
VALUES
(112233,'8-31-2016 08:20:00',19.2),
(112233,'9-01-2016 08:30:00',24),
(112233,'9-01-2016 08:40:00',25),
(112233,'9-01-2016 09:20:00',26),
(112233,'9-02-2016 10:20:00',27),
(112233,'9-02-2016 10:20:00',24),
(112233,'9-02-2016 10:20:00',23),
(112233,'9-02-2016 11:20:00',22),
(112233,'9-02-2016 12:20:00',21),
(112233,'9-02-2016 13:20:00',20),
(112233,'9-03-2016 13:20:00',19.8),
(112233,'9-04-2016 13:20:00',21),
(112233,'9-04-2016 14:20:00',24),
(112233,'9-04-2016 16:20:00',24.6),
(112233,'9-04-2016 19:20:00',26.3),
(112233,'9-04-2016 23:20:00',27),
(112233,'9-05-2016 00:20:00',22),
(112233,'9-06-2016 16:20:00',19.3),
(112233,'9-07-2016 00:20:00',22),
(112233,'9-08-2016 00:20:00',21),
(112233,'9-09-2016 00:20:00',23),
(445566,'9-10-2016 00:20:00',24),
(445566,'9-11-2016 00:20:00',25),
(445566,'9-12-2016 00:20:00',26),
(445566,'9-13-2016 00:20:00',24),
(445566,'9-14-2016 00:20:00',23),
(445566,'9-15-2016 00:20:00',24),
(445566,'9-16-2016 00:20:00',21),
(445566,'9-17-2016 00:20:00',20),
(445566,'9-18-2016 00:20:00',18.5),
(445566,'9-19-2016 00:20:00',17)
image of the table:
Well, I couldn't think of anything simpler. Here's my try to solve the problem:
;with NumOfHrs_rn as (
select id, [Date], Value_ID,
row_number() over (partition by id order by [date]) AS rn
from [dbo].[NumOfHrs]
), NumOfHrs_lag as (
select t1.id, t1.[date],
t2.Value_ID as prev_value,
t1.Value_ID as curr_value
from NumOfHrs_rn as t1
-- get previous value (lag)
join NumOfHrs_rn as t2 on t1.id = t2.id and t1.rn = t2.rn + 1
), NumOfHrs_flag as (
select id, [Date], prev_value, curr_value,
case
when curr_value >= 25 and prev_value < 25 then 'start'
when curr_value <= 20 and prev_value > 20 then 'stop'
else 'ignore'
end as flag
from NumOfHrs_lag
), NumOfHrs_grp as (
select id, [Date], curr_value, flag,
row_number() over (partition by id order by [Date]) -
case flag
when 'start' then 0
when 'stop' then 1
end as grp
from NumOfHrs_flag
where flag in ('start', 'stop')
)
select min([Date]) AS 'start', max([Date]) as 'stop'
from NumOfHrs_grp
group by id, grp
order by min([Date])
Output:
start stop
------------------------------------------------
2016-09-01 08:40:00.000 2016-09-02 13:20:00.000
2016-09-04 19:20:00.000 2016-09-06 16:20:00.000
2016-09-11 00:20:00.000 2016-09-17 00:20:00.000
You can manipulate the above query in order to get the time difference expressed in hours/minutes/seconds format.
Demo here

What will be the best possible way to find date difference?

I have a table for operators in which I want to calculate the time difference between two status (10-20) for the whole day .
Here I want the time difference between "ActivityStatus" 10 and 20.
we have total 3 bunch of 10-20 status in this pic. for last status there is no 20 status in this case it will take the last oa_createdDate (ie oa_id 230141).
My expected output for this operator is date diff between cl_id 230096 and 230102 , date diff between cl_id 230103 and 230107 , date diff between cl_id 230109 and cl_id 230141. Once I get these difference I want to sum all the date diff value to calculate busy time for that operator.
Thanks in advance .
I have a sneaking suspicion that the DateDiff() function is the function that you seek
http://www.w3schools.com/sql/func_datediff.asp
There's an easy way to do what I assume you want done with outer apply, like so:
select tmin.*, t.oa_CreateDate oa_CreateDate_20
, datediff(minute, tmin.oa_CreateDate, t.oa_CreateDate) DiffInMinutes
from testtable t
cross apply
(select top 1 *
from testtable tmin
where tmin.oa_CreateDate < t.oa_CreateDate and tmin.oa_OperatorId = t.oa_OperatorId
order by tmin.oa_CreateDate asc) tmin
where t.ActivityStatus = 20
and t.oa_CreateDate < (select min(oa_CreateDate) from testtable where ActivityStatus = 10 and oa_OperatorId = 1960)
and t.oa_OperatorId = 1960
union all
select t.*
, coalesce(a.oa_CreateDate,ma.MaxDate) oa_CreateDate_20
, datediff(minute, t.oa_CreateDate, coalesce(a.oa_CreateDate,ma.MaxDate)) DiffInMinutes
from testtable t
outer apply
(select top 1 a.oa_CreateDate
from testtable a
where a.oa_OperatorId = t.oa_OperatorId and a.ActivityStatus = 20
and t.oa_CreateDate < a.oa_CreateDate order by a.oa_CreateDate asc) a
outer apply
(select max(a2.oa_CreateDate) maxDate
from testtable a2
where a2.oa_OperatorId = t.oa_OperatorId
and t.oa_CreateDate < a2.oa_CreateDate) ma
where oa_OperatorId = 1960
and ActivityStatus = 10
order by oa_CreateDate asc, oa_CreateDate_20 asc
You can see the fiddle here.
But of course, you have to give us the format / accurracy for the datediff comparison. And this assumes you will always have both Status 10 AND 20, and that their timestamp ranges never overlap.
EDIT: Updated the answer based on your comment, check the new script and fiddle. Now the script fill find all Status 10 - 20 datediffs, and in case no Status 20 exists after the last 10, then the latest existing timestamp after that Status 10 will be used instead.
EDIT 2: Updated with your comment below. But at this point the script is getting rather ugly. Unfortunately I don't have the time to clean it up, so I ask that next time you post a question, please make it as clear cut and clean as possible, since there's a lot less effort involved to answer a question once instead of editing 3 different variations along the ride. :)
This should work anyhow, the new section before the UNION ALL in the script will return results only if there are any Status 20's without preceding 10's. Otherwise it'll return nothing, and move to the main portion of the script as before. Fiddle has been updated as well.
This is one way of doing it.
The first OUTER APPLY will retrieve the next row with a status of 20 that is after the current created datetime.
The second OUTER APPLY will retrieve the next row after the current created datetime where there is no status 20.
SELECT
o.*
, COALESCE(NextStatus.oa_CreateDate, NextStatusIsNull.oa_CreateDate) AS NextTimestamp
, COALESCE(NextStatus.ActivityStatus, NextStatusIsNull.ActivityStatus) AS NextStatus
, DATEDIFF(MINUTE, o.oa_CreateDate,
COALESCE(NextStatus.oa_CreateDate, NextStatusIsNull.oa_CreateDate))
AS DifferenceInMinutes
FROM
operators AS o
OUTER APPLY
(
SELECT TOP 1
oa_CreateDate
, ActivityStatus
FROM
operators
WHERE
ActivityStatus = 20
AND oa_CreateDate > o.oa_CreateDate
ORDER BY
oa_CreateDate
) AS NextStatus
OUTER APPLY
(
SELECT TOP 1
oa_CreateDate
, ActivityStatus
FROM
operators
WHERE
NextStatus.oa_CreateDate IS NULL
AND oa_CreateDate > o.oa_CreateDate
ORDER BY
oa_CreateDate
) AS NextStatusIsNull
WHERE
ActivityStatus = 10
I have used some different test data because you used a picture from which I was unable to cut and paste. This should be easy to convert to your table:
Note this should also work with the none-existing start and end dates,
Also note this was done without any joins to optimize performance.
Test table and data:
DECLARE #t table(ActivityStatus int, oa_createdate datetime, oa_operatorid int)
INSERT #t values
(30, '2015-07-23 08:20', 1960),(20, '2015-07-23 08:24', 1960),
(10, '2015-07-23 08:30', 1960),(20, '2015-07-23 08:40', 1960),
(10, '2015-07-23 08:50', 1960),(50, '2015-07-23 09:40', 1960)
Query:
;WITH cte as
(
SELECT
ActivityStatus,
oa_createdate,
oa_operatorid
FROM #t
WHERE ActivityStatus in (10,20)
UNION ALL
SELECT 20, max(oa_createdate), oa_operatorid
FROM #t
GROUP BY oa_operatorid
HAVING
max(case when ActivityStatus = 20 then oa_createdate end) <
max(case when ActivityStatus = 10 then oa_createdate end)
UNION ALL
SELECT 10, min(oa_createdate), oa_operatorid
FROM #t
GROUP BY oa_operatorid
HAVING
min(case when ActivityStatus = 20 then oa_createdate end) <
min(case when ActivityStatus = 10 then oa_createdate else '2999-01-01' end)
)
SELECT
cast(cast(sum(case when activitystatus = 10 then -1 else 1 end
* cast(oa_createdate as float)) as datetime) as time(0)) as difference_in_time,
oa_operatorid
FROM cte
GROUP BY oa_operatorid
Result:
difference_in_time oa_operatorid
01:04:00 1960
Data
create table #Table2 (oa_id int, oa_OperatorId int, ActivityStatus int, oa_CreateDate datetime)
insert into #Table2
values (1, 1960,10,'2015-08-10 10:55:12.317')
,(2, 1960,20,'2015-08-10 11:55:12.317')
,(3, 1960,30,'2015-08-10 14:55:12.317')
,(4, 1960,50,'2015-08-10 14:58:12.317')
,(5, 1960,10,'2015-08-10 15:55:12.317')
,(6, 1960,20,'2015-08-10 16:20:12.317')
,(7, 1960,10,'2015-08-10 16:30:12.317')
,(8, 1960,50,'2015-08-10 17:20:12.317')
Populate target table with the rows we are interested in
select oa_id,
oa_operatorid,
ActivityStatus,
oa_createDate,
rn = row_number() over (order by oa_id desc)
into #Table
from #Table2
where ActivityStatus in (10, 20)
insert #Table
select top 1
oa_id,
oa_operatorid,
ActivityStatus,
oa_createDate,
0
from #Table2
order by oa_id desc
select * into #Table10 from #Table where ActivityStatus = 10
select * into #Table20 from #Table where ActivityStatus = 20
union
select * from #Table where rn = 0 /*add the last record*/
except
select * from #Table where rn = (select max(rn) from #Table) /**discard the first "20" record*/
/*free time info*/
select datediff(second, t10.oa_createDate, t20.oa_createDate) secondssincelast10,
t20.*
from #Table10 t10 join #Table20 t20
on t10.rn = t20.rn + 1
and t10.oa_OperatorId = t20.oa_OperatorId
/*Summarized info per operator*/
select sum(datediff(second, t10.oa_createDate, t20.oa_createDate)) totalbusytime,
t20.oa_OperatorId
from #Table10 t10 join #Table20 t20
on t10.rn = t20.rn + 1
and t10.oa_OperatorId = t20.oa_OperatorId
group by t20.oa_OperatorId
Best way
DATEDIFF(expr1,expr2)
Example:
CREATE TABLE pins
(`id` int, `time` datetime)
;
INSERT INTO pins
(`id`, `time`)
VALUES
(1, '2013-11-15 05:25:25')
;
SELECT DATEDIFF(CURDATE(), `time`)
FROM `pins`

Sum the timegap if consecutive timestamps are same for 10 min

i am using the below query to find the null values of a column and get the starttime and endtime of the null values using the below query for some 30,000 rows
SELECT
yt1.[timestamp] AS StartTime,
MIN(yt2.[timestamp]) AS EndTime,
DATEDIFF(MINUTE, yt1.[timestamp], MIN(yt2.[timestamp])) AS DifferenceInMinutes
FROM
Sheet1$ yt1
LEFT JOIN Sheet1$ yt2 ON yt1.[timestamp] < yt2.[timestamp]
WHERE
yt1.TWSPD IS NULL
GROUP BY yt1.[timestamp]
The output is
Start time Endtime DifferenceInMinutes
2012-05-18 20:47:03.000 2012-05-18 20:57:04.000 10
2012-05-18 20:57:04.000 2012-05-18 21:07:04.000 10
2012-05-21 18:25:26.000 2012-05-21 18:35:26.000 10
2012-06-07 17:36:28.000 2012-06-07 17:46:28.000 10
2012-06-07 17:46:28.000 2012-06-07 17:56:28.000 10
2012-06-07 17:56:28.000 2012-06-07 18:06:28.000 10
And for example now i need the output as (removed some row to display better)
Start time Endtime DifferenceInMinutes
2012-05-18 20:47:03.000 2012-05-18 21:07:04.000 20
2012-05-21 18:25:26.000 2012-05-21 18:35:26.000 10
2012-06-07 17:36:28.000 2012-06-07 18:06:28.000 30
The timestamp is for every 10 min, if the null values for consecutive 10min timegap should be added and the starttime and endtime should be displayed as from the first null to last null of consecutive timestamp. Hope the question is clear. Please let me know if i am not clear. Please help
SELECT
yt1.[timestamp] AS StartTime,
MIN(yt2.[timestamp]) AS EndTime,
DATEDIFF(MINUTE, yt1.[timestamp], MIN(yt2.[timestamp])) AS DifferenceInMinutes
into #tmp1
FROM
Sheet1$ yt1
LEFT JOIN Sheet1$ yt2 ON yt1.[timestamp] < yt2.[timestamp]
WHERE
yt1.TWSPD IS NULL
GROUP BY yt1.[timestamp]
Select t1.*
into #tmp2
from #tmp1 t1
left join #tmp1 t2 on t1.Starttime=t2.Endtime
where t2.Endtime is null
Declare #rcn int
Select #rcn=1
While #rcn>0
begin
Update #tmp2 set #tmp2.Endtime=t.endTime,#tmp2.DifferenceInMinutes=#tmp2.DifferenceInMinutes+t.DifferenceInMinutes
from #tmp1 t
where t.Starttime=#tmp2.Endtime
select #rcn=##Rowcount
end
select * from #tmp2
Drop Table #tmp1
Drop Table #tmp2
If you want to query your original table to have grouped output - you can do this:
;with
CTE_start
as
(
select T.timestamp, row_number() over(order by T.timestamp) as RowNum
from temp1 as T
where
not exists
(
select *
from temp1 as TT
where TT.timestamp < T.timestamp and TT.timestamp >= dateadd(mi, -11, T.timestamp)
)
),
CTE_end
as
(
select T.timestamp, row_number() over(order by T.timestamp) as RowNum
from temp1 as T
where
not exists
(
select *
from temp1 as TT
where TT.timestamp > T.timestamp and TT.timestamp <= dateadd(mi, 11, T.timestamp)
)
)
select
s.timestamp as [Start time],
e.timestamp as [End time],
datediff(mi, s.timestamp, e.timestamp) as [DifferenceInMinutes]
from CTE_start as s
inner join CTE_end as e on e.RowNum = s.RowNum
SQL FIDDLE EXAMPLE
Another good one, but you have to copy your data into temporary (variable) table
declare #tmp table (timestamp datetime, RowNum int primary key)
insert into #tmp
select T.timestamp, row_number() over(order by T.timestamp) as RowNum
from temp1 as T
;with CTE
as
(
select T.timestamp, T.RowNum, 1 as GroupNum
from #tmp as T
where RowNum = 1
union all
select
T.timestamp, T.RowNum,
C.GroupNum + case when datediff(mi, C.timestamp, T.timestamp) >= 11 then 1 else 0 end
from #tmp as T
inner join CTE as C on C.RowNum + 1 = T.RowNum
)
select
min(C.timestamp) as [Start time],
max(C.timestamp) as [End time],
datediff(mi, min(C.timestamp), max(C.timestamp)) as [DifferenceInMinutes]
from CTE as C
group by C.GroupNum
SQL FIDDLE EXAMPLE