How to display event hour by hour in SQL? - sql

Create table tblEvent ( Event_ID int, Start_Time datetime, End_Time datetime )
insert into tblEvent values(1,'2015-02-10 9:00:00.000','2015-02-10 11:00:00.000')
insert into tblEvent values(2,'2015-02-10 11:00:00.000','2015-02-10 11:20:00.000')
insert into tblEvent values(3,'2015-02-10 11:20:00.000','2015-02-10 13:00:00.000')
and want to be display like below
Hour Event_ID [Start_End]
9 1 9:00-10:00
10 1 10:00-11:00
11 2 11:00-11:20
11 3 11:20-12:00
12 3 12:00-13:00
and we can make the End_Time of Event 3 become 13:30
we had to be display
13 3 13:00-13:30
Can anyone help me?

You can use DATEPART function
DATEPART(HOUR, [Start_End]) AS Hour

select blocks.Hour, e.Event_Id,
format(case when e.Start_Time > blocks.Start_Time then e.Start_Time else blocks.Start_Time end, 'HH:mm') +
'-' +
format(case when e.End_Time < blocks.End_Time then e.End_Time else blocks.End_Time end, 'HH:mm')
from
tblEvent as e inner join
(
select
d0.n + d1.n * 4 as Hour,
dateadd(hh, d0.n + d1.n * 4, cast(cast(current_timestamp as date) as datetime)) as Start_Time,
dateadd(hh, d0.n + d1.n * 4 + 1, cast(cast(current_timestamp as date) as datetime)) as End_Time
from
(select 0 as n union all select 1 union all select 2 union all select 3) as d0,
(select 0 as n union all select 1 union all select 2 union all select 3 union all select 4 union all select 5) as d1
) as blocks
on blocks.End_Time > e.Start_Time and blocks.Start_Time < e.End_Time
order by Event_Id, Hour
Here's a start. SQL Server? Is current day enough? You don't have the format() on SQL 2008 so you'll have to do that part yourself.
I'm not sure this handles all the cases exactly the way you want. You can take the basic idea and extend it across a longer range of hours, say 168 for a full week.
http://sqlfiddle.com/#!6/819c0/9

TRY Some thing like this.This sample data is running ok.
Please provide another sample data atleast 10 rows and don't forget to paste desired output.
Also read my comment in script.
DECLARE #tblEvent TABLE (
Event_ID INT
,Start_Time DATETIME
,End_Time DATETIME
)
INSERT INTO #tblEvent
VALUES (
1
,'2015-02-10 9:00:00.000'
,'2015-02-10 11:00:00.000'
)
,(
2
,'2015-02-10 11:00:00.000'
,'2015-02-10 11:20:00.000'
)
,(
3
,'2015-02-10 11:20:00.000'
,'2015-02-10 13:00:00.000'
);
--select *,DATEdiff(hour,a.Start_Time,a.End_Time) from #tblEvent a
;
WITH CTE
AS (
SELECT *
,ROW_NUMBER() OVER (
ORDER BY Start_Time
) RN
,DATEdiff(hour, Start_Time, End_Time) Diff
FROM #tblEvent
)
--select * from cte
,CTE1
AS (
SELECT Event_ID
,Start_Time
,CASE
WHEN Diff > 1
THEN DATEADD(minute, 60 - DATEPART(minute, Start_Time), Start_Time)
ELSE End_Time
END End_Time
,RN
,DIFF
,1 RN1
,DATEPART(minute, Start_Time) DIFFMIN
FROM CTE
--WHERE RN = 1
UNION ALL
SELECT CASE
WHEN A.Diff > B.DIFF
THEN b.Event_ID
ELSE a.Event_ID
END
,B.End_Time Start_Time
,CASE
WHEN A.Diff > B.DIFF
THEN DATEADD(minute, 60 - DATEPART(minute, B.Start_Time), B.End_Time)
ELSE A.End_Time
END End_Time
,CASE
WHEN A.Diff > B.DIFF
THEN B.RN
ELSE B.RN + 1
END RN
,CASE
WHEN A.Diff > B.DIFF
THEN B.DIFF - 1
ELSE A.Diff
END
,RN1 + 1
,0
FROM CTE1 B
CROSS APPLY (
SELECT *
FROM CTE
WHERE RN = B.RN
) A
WHERE B.DIFF > 0
)
SELECT [Hour]
,Event_ID
,[Start_End]
FROM (
SELECT DATEPART(HOUR, Start_Time) [Hour]
,ROW_NUMBER() OVER (
PARTITION BY Start_Time ORDER BY Start_Time
) RN2
,Event_ID
,CONVERT(VARCHAR(5), Start_Time, 114) + '-' + CONVERT(VARCHAR(5), End_Time, 114) [Start_End]
FROM CTE1
) TBL
WHERE RN2 = 1
--BELOW QUERY RETURN 6 ROWS
-- I AM TRYING TO ELIMINATE THE EXTRA ROWS WITHOUT ROW_NUMBER
--WHICH WOULD BE MORE OPTIMIZE,BUT I AM NOT GETTING WHAT ACTUALLY CAUSING THIS BEHAVIOUR
--MEANWHILE YOU CAN TEST OTHER SAMPLE DATA,AND THROW OTHER SAMPLE DATA
--SELECT DATEPART(HOUR, Start_Time) [Hour]
-- ,Event_ID
-- ,CONVERT(VARCHAR(5), Start_Time, 114) + '-' + CONVERT(VARCHAR(5), End_Time, 114) [Start_End]
-- FROM CTE1

Related

Error message: Maximum Recursion exhausted even with OPTION( MAXRECURSION 0)

I'm creating a function that will have as input a start date and a number of minutes. The function will add the number of minutes to the start date and it will output an end date, but only considering work hours and excluding weekends and holidays.
You can see part of the function below.
ALTER FUNCTION [dbo].[DataFimPrevisto] (#tempoPrevisto real, #DataIni datetime)
RETURNS datetime
WITH EXECUTE AS CALLER
AS
BEGIN
DECLARE #DataFim datetime;
DECLARE #calculo TABLE( xend datetime, [minutes] int);
WITH
drange (date_start, date_end) AS
(
SELECT
CAST(#DataIni AS DATE) AS date_start,
CAST(DATEADD( YEAR, 1, #DataIni) AS DATE) AS date_end
),
dates0 (adate, date_end) AS
(
SELECT date_start, date_end FROM drange
UNION ALL
SELECT DATEADD(day, 1, adate), date_end FROM dates0 WHERE adate < date_end
),
dates (adate) AS
(
SELECT adate FROM dates0
WHERE DATEPART(dw , adate) NOT IN ('1', '7') AND NOT EXISTS( SELECT 1 FROM BAS_PeriodosExcecoes B WHERE B.Trabalhavel = 0 AND B.DataInicio = adate)
),
hours (hour_start, hour_end) AS
(
SELECT 8.5*60, 12.5*60
UNION
SELECT 13.5*60, 18*60
),
hours_friday (hour_start, hour_end) AS
(
SELECT 8*60, 14*60
),
datehours (xstart, xend) AS
(
SELECT *
FROM
(
SELECT
DATEADD(minute, hour_start, CAST(adate AS datetime)) xstart,
DATEADD(minute, hour_end , CAST(adate AS datetime)) xend
FROM dates AS d, hours AS h
WHERE DATEPART(dw , adate) <> '6'
UNION
SELECT T2.xstart, T2.xend
FROM
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY T.xstart ORDER BY T.xend ASC) AS rank
FROM
(
SELECT
#DataIni xstart,
DATEADD(minute, hour_end, CAST(adate AS datetime)) xend
FROM dates AS d, hours AS h
WHERE adate = CAST( #DataIni AS DATE) AND DATEADD(minute, hour_end, CAST(adate AS datetime)) > #DataIni AND DATEPART(dw , adate) <> '6'
) T
) T2
WHERE T2.rank = 1
UNION
SELECT
DATEADD(minute, hour_start, CAST(adate AS datetime)) xstart,
DATEADD(minute, hour_end , CAST(adate AS datetime)) xend
FROM dates AS d, hours_friday AS h
WHERE DATEPART(dw , adate) = '6'
UNION
SELECT T2.xstart, T2.xend
FROM
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY T.xstart ORDER BY T.xend ASC) AS rank
FROM
(
SELECT
#DataIni xstart,
DATEADD(minute, hour_end, CAST(adate AS datetime)) xend
FROM dates AS d, hours_friday AS h
WHERE adate = CAST( #DataIni AS DATE) AND DATEADD(minute, hour_end, CAST(adate AS datetime)) > #DataIni AND DATEPART(dw , adate) = '6'
) T
) T2
WHERE T2.rank = 1
) T3 WHERE T3.xstart >= #DataIni
),
cumulative (xend, [minutes]) AS
(
SELECT t.xend, SUM(DATEDIFF(MINUTE, xstart, xend)) OVER (ORDER BY xstart) AS [minutes]
FROM datehours AS t
)
INSERT INTO #calculo
SELECT TOP 1 xend, [minutes]
FROM cumulative
WHERE [minutes] >= #tempoPrevisto
ORDER BY cumulative.xend ASC;
SET #DataFim = (SELECT DATEADD( MINUTE, #tempoPrevisto - MAX([minutes]), MAX( [xend])) FROM #calculo);
RETURN(#DataFim);
END;
When I execute this function with
SELECT dbo.DataFimPrevisto( 21240, DATETIMEFROMPARTS( 2023, 1, 25, 6, 0, 0, 0)) OPTION(MAXRECURSION 0);
SSMS returns the error message
The maximum recursion 100 has been exhausted before statement completion
Even tho I'm using OPTION(MAXRECURSION 0).

How to use Join query between CTE and two tables?

I want to use a join query in CTE between two tables EmployeeDetails and MachineAttendance, but I'm confused - how will it be used?
;With CTE As
(
select ed.EmpName
from EmployeeDetails ed
)
,cte1; AS
(
select
ma.EmpID, cast(MA.Datetime as Date) as [Date],
case when MA.INOUT = 1 then DateTime end as INOUT_INTIME,
case when MA.INOUT = 2 then DateTime end as INOUT_OUTTIME
from
MachineAttendance MA
), cte2 as
(
select
EmpID, [Date],
MAX(INOUT_INTIME) AS INTIME,
MAX(INOUT_OUTTIME) AS OUTTIME,
DATEDIFF(Hour, MAX(INOUT_INTIME), MAX(INOUT_OUTTIME)) as [Hours]
from
CTE1
group by
EmpID, [Date]
)
select
EmpID, [Date], INTIME, OUTTIME, [Hours],
case
when [Hours] >= 8 then 1
when [Hours] = 0 then 0
when [Hours] >= 6 then 0.5
end as [Day],
case
when [Hours] > 8 then [Hours] - 8
else 0
end as OT,
case
when [Hours] >= 8
then ([Hours] - 8) * 100 else 0
end as OTAMount,
Convert(varchar(10), Date, 120) as [Date],
Convert(varchar(10), INTIME, 108) as [Time],
Case
When Convert(Time, INTIME, 108) > '09:10'
Then 1 else 0
end as Late
from
cte2
inner join
cte On cte.EmpID = cte2.EmpID`
You typed mixed syntax mysql and sql server and some extra comma was ther i think below will work
;With CTE As
(
select ed.EmpName from EmployeeDetails ed
)
,cte1 AS
(
SELECT ma.EmpID, CAST(MA.Datetime as Date) AS [Date],
CASE WHEN MA.INOUT = 1 THEN DateTime END AS INOUT_INTIME,
CASE WHEN MA.INOUT = 2 THEN DateTime END AS INOUT_OUTTIME
From
MachineAttendance MA
),
cte2
as
(
select EmpID, [Date], MAX(INOUT_INTIME) AS INTIME,
MAX(INOUT_OUTTIME) AS OUTTIME
, DATEDIFF(Hour, MAX(INOUT_INTIME), MAX(INOUT_OUTTIME)) as [Hours]
FROM CTE1
GROUP BY EmpID, [Date]
)
select EmpID, [Date], INTIME, OUTTIME, [Hours]
, CASE WHEN [Hours] >= 8 THEN 1
WHEN [Hours] = 0 THEN 0
WHEN [Hours] >= 6 THEN 0.5 END AS [Day],
CASE WHEN [Hours] > 8 then [Hours] - 8 else 0 End as OT,
CASE WHEN [Hours] >= 8
then ([Hours] - 8) * 100 else 0 END AS OTAMount,
Convert(varchar(10),Date,120) as [Date],
Convert(varchar(10),INTIME,108) as [Time],
Case When Convert(Time,INTIME,108) > '09:10' Then 1 else 0 end as Late
from cte2
INNER Join cte On cte.EmpID=cte2.EmpID

distribute accumulated working hours through days

I have Date time when engine has started working and how long was it working. but sometimes it can work more than 24 Hours.
if it worked for 28 Hours on the starting date i will have record
Name started_working Finished working hours_worked
obj-00123 07/02/2018 13:30 08/02/2018 17:30 28
I need to to have record that will show that engine has worked for 10:30 in 07 and 17:30 in 08.
Name started_working Finished working hours_worked
obj-00123 07/02/2018 13:30 07/02/2018 00:00 10:30
obj-00123 07/02/2018 13:30 08/02/2018 17:30 17:30
or something like that. I don't have any idea how can i get this done. can you give me some clues. i dont ask for writing code if its not too easy.
thank you
This might do the trick for you
--Using CTE to show sample data
;WITH cteX( Name,started_working,Finished_working)
AS
(
SELECT
'obj-00123','07/02/2018 13:30','08/02/2018 17:30' UNION ALL
SELECT 'obj-00155','07/02/2018 15:00','07/02/2018 22:30'
)
SELECT
X.Name
, X.started_working
, X.Finished_working
, HoursWorked = CONVERT(VARCHAR(12), DATEADD(minute, DATEDIFF(minute, X.started_working, X.Finished_working), 0), 114)
FROM
(
SELECT
T1.Name
,T1.started_working
,Finished_working = DATEADD(SECOND,0,DATEADD(DAY, DATEDIFF(DAY,-1,T1.started_working),0)) -- Dummy finish time # Midnight
FROM
cteX T1
WHERE
DATEDIFF(DAY,T1.started_working,T1.Finished_working) <> 0 --Create a dummy finish time #Midnight when start and end not on same day
UNION ALL
SELECT
T2.Name
,started_working = CASE WHEN DATEDIFF(DAY,T2.started_working,T2.Finished_working) <> 0
THEN DATEADD(DAY, DATEDIFF(DAY, 0, T2.Finished_working), 0) --Start # Midnight
ELSE T2.started_working
END
,T2.Finished_working
FROM
cteX T2
) X
ORDER BY
X.Name, X.started_working
OUTPUT
Name started_working Finished_working HoursWorked
obj-00123 2018-07-02 13:30:00.000 2018-07-03 00:00:00.000 10:30:00:000
obj-00123 2018-08-02 00:00:00.000 2018-08-02 17:30:00.000 17:30:00:000
obj-00155 2018-07-02 15:00:00.000 2018-07-02 22:30:00.000 07:30:00:000
According to your sample data working hours may be more than several days. In this case you need to use tally table or recursive CTE. I have used recursive CTE since it's easier to handle result fields. Also there are two columns in result named started_working and started_working2. started_working is from your expected output, but I believe you need started_working2 column
declare #T as table (
Name varchar(100)
, started_working datetime
, finished_working datetime
--, hours_worked int
)
insert into #T
values
('obj-00123', '20180207 13:30', '20180208 17:30')
, ('obj-00123', '20180208 19:00', '20180209 05:00')
, ('obj-00123', '20180209 19:00', '20180209 22:00')
, ('obj-00123', '20180210 19:00', '20180213 22:00')
;with rcte as (
select
*, started_working2 = started_working
, next_date = cast(dateadd(dd, 1, started_working) as date), 1 step
from
#T
union all
select
Name, started_working, finished_working
, cast(next_date as datetime)
, dateadd(dd, 1, next_date), step + 1
from
rcte
where
next_date < finished_working
)
select
Name, started_working, started_working2, finished_working
, right(replace(str(diff / 60), ' ', 0), 2) + ':' + right(replace(str(diff % 60), ' ', 0), 2) hours_worked
from (
select
Name, started_working
, case
when step = 1 then started_working
else started_working2
end started_working2
, case
when step = max(step) over (partition by Name, started_working)
then finished_working else next_date
end finished_working
from
rcte
) t
cross apply (select datediff(mi, started_working2, finished_working) diff) ca
I'd approach the solution something like this:
WITH dynamic_twelths_of_hr_table(datetime2_value) AS
(
SELECT '2017-01-01'
UNION ALL
SELECT DATEADD(MINUTE, 5, datetime2_value)
FROM dynamic_twelths_of_hr_table
WHERE DATEADD(MINUTE, 5, datetime2_value) <= '2019-01-01'
)
,twelths_hr_table AS
(
SELECT
DATEADD(DAY, DATEDIFF(DAY, 0, datetime2_value), 0) AS date_value
,datetime2_value
FROM dynamic_twelths_of_hr_table
)
,modified_source_table AS
(
SELECT
name
,objectid
,engine_start
,ISNULL(engine_stop, GETDATE()) AS engine_stop
,IIF(engine_start IS NULL OR engine_stop IS NULL, 1, 0) AS is_still_running
FROM [YOUR_SOURCE_TABLE]
)
SELECT
name
,objectid
,is_still_running
,date_value
,(COUNT(datetime2_value)/12.0) AS hours_run_on_this_day
FROM
modified_source_table
LEFT JOIN
twelths_hr_table AS tht
ON (tht.datetime2_value BETWEEN engine_start AND engine_stop)
GROUP BY
name, objectid, is_still_running, date_value
ORDER BY
name, objectid, is_still_running, date_value
Note I haven't tested this code so please excuse any small syntax errors.
I've also baked in an assumption about the range of dates to be considered (these can be widened, or made dynamic based on when the query runs), and it has a 5 minute resolution (based on the fact that, at a glance, I could only see one value in the engine_stop column that didn't fall on a 5-minute threshold - so I assume sub-5-minute precision is not required).
Basically what it does is expand each engine row out into 5-minute windows (twelths of an hour), and then simply groups these by day and counts the number of windows per day during which the engine was running.
For currently-running engines, it will calculate how long it has run so far. I trust you can tweak the code to your exact requirements.
thank you to all. this worked perfectly. it needed slight polishing and recursion needed to be set to 0.
But creating view is a trouble with CTE.
create view mroobjectenginerowkinghoursdeclare as
declare #T as table (
Name nvarchar(100)
, OBJECTID varchar(50)
, started_working datetime
,STOPFROM datetime
,STARTDATE datetime
,STOPDATE datetime
,MODIFIEDDATETIME datetime
,START_STOP int
,STARTDESCRIPTION nvarchar(300)
,STOPDESCRIPTION nvarchar(300)
,wattage nvarchar (50)
,purpose nvarchar(300)
,location nvarchar(300)
,finished_working datetime
,oldDiff int
)
insert into #T
select
NAME
,OBJECTID
,STOPTO
,STOPFROM
,STARTDATE
,STOPDATE
,MODIFIEDDATETIME
,START_STOP
,STARTDESCRIPTION
,STOPDESCRIPTION
,wattage
,purpose
,location
,next_stopfrom
,diff
FROM [MicrosoftDynamicsAX].[dbo].[mroobjectengineworkinghours]
;with rcte as (
select
*, started_working2 = started_working
, next_date = cast(dateadd(dd, 1, started_working) as date), 1 step
from
#T
union all
select
Name,OBJECTID, started_working,STOPFROM,STARTDATE,STOPDATE,MODIFIEDDATETIME,START_STOP,STARTDESCRIPTION
,STOPDESCRIPTION,wattage
,purpose
,location, finished_working,oldDiff
, cast(next_date as datetime)
, dateadd(dd, 1, next_date), step + 1
from
rcte
where
next_date < finished_working
)
select
Name,OBJECTID, started_working,STOPFROM,STARTDATE,STOPDATE,MODIFIEDDATETIME,START_STOP,STARTDESCRIPTION
,STOPDESCRIPTION,wattage
,purpose
,location,oldDiff, started_working2, finished_working
, right(replace(str(diff / 60), ' ', 0), 2) + ':' + right(replace(str(diff % 60), ' ', 0), 2) hours_worked
from (
select
Name,OBJECTID, started_working,STOPFROM,STARTDATE,STOPDATE,MODIFIEDDATETIME,START_STOP,STARTDESCRIPTION
,STOPDESCRIPTION,wattage
,purpose
,location,oldDiff
, case
when step = 1 then started_working
else started_working2
end started_working2
, case
when step = max(step) over (partition by Name, started_working)
then finished_working else next_date
end finished_working
from
rcte
) t
cross apply (select datediff(mi, started_working2, finished_working) diff) ca
OPTION (MAXRECURSION 0);

7 day average in SQL Server 2014

I need to modify the following T-SQL statement to include a rolling 7 day average of the revenue.
What do I need to include in the following code to achieve that?
SELECT
CAST(create_dtg AS DATE) DATE,
SUM([agent_rev] + [anchor_rev] + [corp_rev] + [offsite_rev]) AS RevenueTotals,
SUM([media_est_cost] + [other_cost]) AS COSTTOTALS
FROM
[dbo].[dw_rpt_traffic]
WHERE
[create_dtg] >= ( Getdate() - 90 )
--GROUP BY CREATE_DTG
--ORDER BY CREATE_DTG ASC
I also tried using Parttion by, however, this returns the same value as the Revenuetotals.
Select a.dte, a.revenuetotals, a.COSTTOTALS, AVG(A.RevenueTotals) OVER (PARTITION BY a.dte ORDER BY a.dte ROWS 7 PRECEDING) as Day7Avg
from
(
select CAST(CREATE_DTG AS DATE) as dte,
SUM([AGENT_REV]+[ANCHOR_REV]+[CORP_REV]+[OFFSITE_REV]) as RevenueTotals,
SUM([MEDIA_EST_COST]+[OTHER_COST]) as COSTTOTALS
FROM [dbo].[dw_rpt_traffic]
where [CREATE_DTG] >= (GetDate() - 90)
GROUP BY CREATE_DTG
) as A
Group by a.dte, a.revenuetotals, a.COSTTOTALS
order by a.dte
Thanks, Karen
For rolling aggregates I typically use an OVER clause with ROWS [...] PRECEDING [...].
WITH cte
AS ( SELECT x.Date
,x.Revenue
,AVG(x.Revenue) OVER ( ORDER BY x.Date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS [MA7]
FROM ( SELECT CAST(t.Date AS DATE) AS [Date]
,SUM(t.Revenue) AS [Revenue]
FROM #tmp t
WHERE CAST(t.Date AS DATE) > CAST(GETDATE() - 96 AS DATE)
GROUP BY CAST(t.Date AS DATE)
) x
)
SELECT c.Date
,c.Revenue
,c.MA7
FROM cte c
WHERE c.Date > CAST(GETDATE() - 90 AS DATE)
ORDER BY c.Date;
The table above was generated with the following:
IF ( OBJECT_ID('tempdb..#tmp') IS NOT NULL )
DROP TABLE #tmp;
CREATE TABLE #tmp
(
[Date] DATETIME
,[Revenue] DECIMAL(18, 2)
);
--
DECLARE #first INT = 0
,#last INT = 200;
WHILE #first < #last
BEGIN
INSERT INTO #tmp
( Date, Revenue )
VALUES ( GETDATE() - #first * 0.5, RAND() * 100000 );
SET #first = #first + 1;
END;
Probably the easiest way uses outer apply:
with rt as (
select CAST(CREATE_DTG AS DATE) as dte,
SUM([AGENT_REV]+[ANCHOR_REV]+[CORP_REV]+[OFFSITE_REV]) as RevenueTotals,
SUM([MEDIA_EST_COST]+[OTHER_COST]) as COSTTOTALS
from [dbo].[dw_rpt_traffic]
where [CREATE_DTG] >= (GetDate() - 90)
)
select rt.*, rolling.avgrt
from rt outer apply
(select avg(rt2.RevenueTotals) as avgrt
from rt rt2
where rt2.dte >= dateadd(day, -6, rt.dte) and
rt2.dte <= rt.dte
) rolling
order by dte;

SQL Server, calculating total hours per day with multiple breaks

I am creating a query to get the total hours elapsed in a day by someone, however there can be multiple breaks in the times per day.
Here is the query that I have at the moment.
SELECT
CHINA_VISION_DorEvents.DorCtrls_Ref,
CHINA_VISION_PubCards.CardCode,
CHINA_VISION_DorEvents.EventTM
FROM
CHINA_VISION_PubCards
INNER JOIN
CHINA_VISION_DorEvents ON CHINA_VISION_PubCards.CardCode = CHINA_VISION_DorEvents.CardCode
WHERE
(CHINA_VISION_PubCards.CardCode = '000006f1')
AND CHINA_VISION_DorEvents.DorCtrls_Ref = '16'
ORDER BY
CONVERT(Date,CHINA_VISION_DorEvents.EventTM) DESC
This query doesn't currently attempt to work out the elapsed time, but here are the results of this so you can see how the data looks.
Ref CardCode EventTM
---------------------------------------
16 000006f1 2015-01-27 07:32:35.000
16 000006f1 2015-01-26 07:38:02.000
16 000006f1 2015-01-26 12:30:54.000
16 000006f1 2015-01-26 13:03:28.000
16 000006f1 2015-01-26 17:28:47.000
16 000006f1 2015-01-23 07:31:10.000
16 000006f1 2015-01-23 12:22:50.000
16 000006f1 2015-01-23 12:47:51.000
16 000006f1 2015-01-23 17:00:20.000
16 000006f1 2015-01-22 07:35:03.000
16 000006f1 2015-01-22 12:28:13.000
16 000006f1 2015-01-22 13:03:12.000
16 000006f1 2015-01-22 16:55:56.000
As you can see most days there are 4 records, and i need to work out the elapsed time for them so for example for the 26
07:38:02
12:30:54
elapsed time = 4 hours, 52 minutes and 52 seconds
13:03:28
17:28:47
Elapsed time = 4 hours, 25 minutes and 19 seconds
So the total elapsed for the 26th would be 9 hours 17 minuets 71
So in the result it would look like
Date Elapsed
2015-01-26 9:17:71
and so on
We do not need to calculate between 2-3 as the user is not logged on on here.
1 2 3 4
think of it like this ON - OFF BACK ON - OFF
table structure
Name type allow null
Reference int Unchecked
DorCtrls_Ref int Checked
EventsID tinyint Checked
EventTM datetime Checked
CardCode varchar(50) Checked
JustificationCode tinyint Checked
RecordIndex bigint Checked
Memo varchar(50) Checked
TempltCard varchar(1024)Checked
Templtlength varchar(32)Checked
TempltDir varchar(50) Checked
If you're not using a very old version of SQL Server, this will work for you:
Test Data:
CREATE TABLE Test(Ref int, CardCode varchar(20), EventTM datetime)
insert into Test
select 16,'000006f1','2015-01-27T07:32:35.000' union all
select 16,'000006f1','2015-01-26T07:38:02.000' union all
select 16,'000006f1','2015-01-26T12:30:54.000' union all
select 16,'000006f1','2015-01-26T13:03:28.000' union all
select 16,'000006f1','2015-01-26T17:28:47.000' union all
select 16,'000006f1','2015-01-23T07:31:10.000' union all
select 16,'000006f1','2015-01-23T12:22:50.000' union all
select 16,'000006f1','2015-01-23T12:47:51.000' union all
select 16,'000006f1','2015-01-23T17:00:20.000' union all
select 16,'000006f1','2015-01-22T07:35:03.000' union all
select 16,'000006f1','2015-01-22T12:28:13.000' union all
select 16,'000006f1','2015-01-22T13:03:12.000' union all
select 16,'000006f1','2015-01-22T16:55:56.000';
Query:
WITH ByDays AS ( -- Number the entry register in each day
SELECT
EventTm AS T,
CONVERT(VARCHAR(10),EventTm,102) AS Day,
FLOOR(CONVERT(FLOAT,EventTm)) DayNumber,
ROW_NUMBER() OVER(PARTITION BY FLOOR(CONVERT(FLOAT,EventTm)) ORDER BY EventTm) InDay
FROM Test
)
--SELECT * FROM ByDays ORDER BY T
,Diffs AS (
SELECT
E.Day,
E.T ET, O.T OT, O.T-E.T Diff,
DATEDIFF(S,E.T,O.T) DiffSeconds -- difference in seconds
FROM
(SELECT BE.T, BE.Day, BE.InDay
FROM ByDays BE
WHERE BE.InDay % 2 = 1) E -- Even rows
INNER JOIN
(SELECT BO.T, BO.Day, BO.InDay
FROM ByDays BO
WHERE BO.InDay % 2 = 0) O -- Odd rows
ON E.InDay + 1 = O.InDay -- Join rows (1,2), (3,4) and so on
AND E.Day = O.Day -- in the same day
)
--SELECT * FROM Diffs
SELECT Day,
SUM(DiffSeconds) Seconds,
CONVERT(VARCHAR(8),
(DATEADD(S, SUM(DiffSeconds), '1900-01-01T00:00:00')),
108) TotalHHMMSS -- The same, formatted as HH:MM:SS
FROM Diffs GROUP BY Day
The result looks like this.
Day Seconds TotalHHMMSS
2015.01.22 31554 08:45:54
2015.01.23 32649 09:04:09
2015.01.26 33491 09:18:11
See the corresponding sql fiddle: http://sqlfiddle.com/#!3/e1d31/1
From your result you have posted in your question, you can try the below code
CREATE TABLE #TEMP(Ref INT,CardCode VARCHAR(40),EventTM DATETIME)
INSERT INTO #TEMP
SELECT 16, '000006f1', '2015-01-27 07:32:35.000'
UNION ALL
SELECT 16, '000006f1', '2015-01-26 07:38:02.000'
UNION ALL
SELECT 16, '000006f1', '2015-01-26 12:30:54.000'
UNION ALL
SELECT 16, '000006f1', '2015-01-26 13:03:28.000'
UNION ALL
SELECT 16, '000006f1', '2015-01-26 17:28:47.000'
UNION ALL
SELECT 16, '000006f1', '2015-01-23 07:31:10.000'
UNION ALL
SELECT 16, '000006f1', '2015-01-23 12:22:50.000'
UNION ALL
SELECT 16, '000006f1', '2015-01-23 12:47:51.000'
UNION ALL
SELECT 16, '000006f1', '2015-01-23 17:00:20.000'
UNION ALL
SELECT 16, '000006f1', '2015-01-22 07:35:03.000'
UNION ALL
SELECT 16, '000006f1', '2015-01-22 12:28:13.000'
UNION ALL
SELECT 16, '000006f1', '2015-01-22 13:03:12.000'
UNION ALL
SELECT 16, '000006f1', '2015-01-22 16:55:56.000'
QUERY
;WITH CTE AS
(
-- Gets row number Order the date
SELECT ROW_NUMBER() OVER( ORDER BY EventTM)RNO, *
FROM #TEMP
)
,CTE2 AS
(
-- Split to hours,minutes and seconds
SELECT C1.*,C2.EventTM EM,DATEDIFF(S,C1.EventTM,C2.EventTM)DD,
cast(
(cast(cast(C2.EventTM as float) - cast(C1.EventTM as float) as int) * 24)
+ datepart(hh, C2.EventTM - C1.EventTM)
as INT)HH
,CAST(right('0' + cast(datepart(mi, C2.EventTM - C1.EventTM) as varchar(2)), 2)AS INT)MM
,CAST(right('0' + cast(datepart(ss, C2.EventTM - C1.EventTM) as varchar(2)), 2)AS INT)SS
FROM CTE C1
LEFT JOIN CTE C2 ON C1.RNO=C2.RNO-1
WHERE C1.RNO % 2 <> 0
),
CTE3 AS
(
-- Sum the hours, minute and seconds
SELECT CAST(EventTM AS DATE)EventTM,
SUM(HH) HH,SUM(MM) MM,SUM(SS) SS
FROM CTE2
GROUP BY CAST(EventTM AS DATE)
)
-- Format the elapsed time
SELECT EventTM,
CASE WHEN MM >=60 THEN CAST(HH+1 AS VARCHAR(10)) END + ':' +
CASE WHEN MM >=60 THEN right('0' + CAST(MM-60 AS VARCHAR(10)),2) END + ':' +
CAST(SS AS VARCHAR(10))Elapsed
FROM CTE3
Click here to view result
EDIT :
From your query, you can use the below code
;WITH CTE AS
(
-- Gets row number Order the date
SELECT ROW_NUMBER() OVER( ORDER BY CONVERT(DateTime,CHINA_VISION_DorEvents.EventTM))RNO,
CHINA_VISION_DorEvents.DorCtrls_Ref Ref,
CHINA_VISION_PubCards.CardCode,
CONVERT(DateTime,CHINA_VISION_DorEvents.EventTM) EventTM
FROM CHINA_VISION_PubCards INNER JOIN
CHINA_VISION_DorEvents ON CHINA_VISION_PubCards.CardCode = CHINA_VISION_DorEvents.CardCode
WHERE (CHINA_VISION_PubCards.CardCode = '000006f1')
and CHINA_VISION_DorEvents.DorCtrls_Ref= '16'
)
,CTE2 AS
(
-- Split to hours,minutes and seconds
SELECT C1.*,C2.EventTM EM,DATEDIFF(S,C1.EventTM,C2.EventTM)DD,
cast(
(cast(cast(C2.EventTM as float) - cast(C1.EventTM as float) as int) * 24)
+ datepart(hh, C2.EventTM - C1.EventTM)
as INT)HH
,CAST(right('0' + cast(datepart(mi, C2.EventTM - C1.EventTM) as varchar(2)), 2)AS INT)MM
,CAST(right('0' + cast(datepart(ss, C2.EventTM - C1.EventTM) as varchar(2)), 2)AS INT)SS
FROM CTE C1
LEFT JOIN CTE C2 ON C1.RNO=C2.RNO-1
WHERE C1.RNO % 2 <> 0
),
CTE3 AS
(
-- Sum the hours, minute and seconds
SELECT CAST(EventTM AS DATE)EventTM,
SUM(HH) HH,SUM(MM) MM,SUM(SS) SS
FROM CTE2
GROUP BY CAST(EventTM AS DATE)
)
-- Format the elapsed time
SELECT EventTM,
CASE WHEN MM >=60 THEN CAST(HH+1 AS VARCHAR(10)) END + ':' +
CASE WHEN MM >=60 THEN right('0' + CAST(MM-60 AS VARCHAR(10)),2) END + ':' +
CAST(SS AS VARCHAR(10))Elapsed
FROM CTE3
Try this,The out put is correct .
your output is wrong.9:17:71 is wrong.it should be 9:18:11.
Declare #t table(Ref int, CardCode varchar(20), EventTM datetime)
insert into #t
select 16,'000006f1','2015-01-27 07:32:35.000' union all
select 16,' 000006f1','2015-01-26 07:38:02.000' union all
select 16,' 000006f1','2015-01-26 12:30:54.000' union all
select 16,' 000006f1','2015-01-26 13:03:28.000' union all
select 16,' 000006f1','2015-01-26 17:28:47.000' union all
select 16,' 000006f1','2015-01-23 07:31:10.000' union all
select 16,' 000006f1','2015-01-23 12:22:50.000' union all
select 16,' 000006f1','2015-01-23 12:47:51.000' union all
select 16,' 000006f1','2015-01-23 17:00:20.000' union all
select 16,' 000006f1','2015-01-22 07:35:03.000' union all
select 16,' 000006f1','2015-01-22 12:28:13.000' union all
select 16,' 000006f1','2015-01-22 13:03:12.000' union all
select 16,' 000006f1','2015-01-22 16:55:56.000'
;with CTE as
(
select *,row_number()over(partition by dateadd(d,0,datediff(d,0,EventTM))
order by EventTM)rn from #t
)
,CTE1 as
(
select Ref,CardCode,EventTM, rn oddrn,0 TimeElapse from CTE where rn%2<>0
union all
select a.Ref,a.CardCode,a.EventTM, rn ,datediff(s,b.EventTM,a.EventTM)
from CTE a
inner join CTE1 b on
dateadd(d,0,datediff(d,0,a.EventTM))= dateadd(d,0,datediff(d,0,b.EventTM))
and a.ref=b.ref
and a.rn-b.oddrn=1 and a.rn%2=0
)
select EventTM,cast((convert(varchar(5),TimeElapse/3600) +':'+
convert(varchar(5),TimeElapse%3600/60)
+':'+ convert(varchar(5),TimeElapse%60)) as datetime) from
(select dateadd(d,0,datediff(d,0,EventTM)) EventTM,sum(TimeElapse) TimeElapse
from cte1
where TimeElapse>0
group by dateadd(d,0,datediff(d,0,EventTM)))tbl
I haven't been able to test this myself but it may give you a good start. I would use a cursor if you want to keep it in SQL although I'd probably prefer to do it in CLR. Others may have a better method but you can try this:
Declare #olddate datetime,
#date datetime
DECLARE #Table table (Ref int, CardCode varchar(20), EventTM datetime, ElapsedTime varchar(30))
Declare cur_mycursor Cursor fast_forward for
SELECT CHINA_VISION_DorEvents.EventTM
FROM CHINA_VISION_PubCards INNER JOIN
CHINA_VISION_DorEvents ON CHINA_VISION_PubCards.CardCode = CHINA_VISION_DorEvents.CardCode
WHERE (CHINA_VISION_PubCards.CardCode = '000006f1')
and CHINA_VISION_DorEvents.DorCtrls_Ref= '16'
Order by CHINA_VISION_DorEvents.EventTM desc
Open cur_mycursor
Fetch next from cur_mycursor into #olddate
While ##FETCH_STATUS = 0
Begin
Fetch next from cur_mycursor into #date
INSERT INTO #Table (Ref, CardCode, EventTM, ElapsedTime)
SELECT CHINA_VISION_DorEvents.DorCtrls_Ref,
CHINA_VISION_PubCards.CardCode,
CHINA_VISION_DorEvents.EventTM,
case when Convert(varchar, #date, 112) = Convert(varchar, CHINA_VISION_DorEvents.EventTM, 112)
then Cast(datediff(mi, #date, CHINA_VISION_DorEvents.EventTM) / 1440 as varchar) + ' days ' +
Cast(datediff(mi, #date, CHINA_VISION_DorEvents.EventTM) % 1440 / 60 as varchar) + ' hours ' +
Cast(datediff(mi, #date, CHINA_VISION_DorEvents.EventTM) % 1440 %60 as varchar) + ' minutes'
else '0'
end as elapsedtime
FROM CHINA_VISION_PubCards INNER JOIN
CHINA_VISION_DorEvents ON CHINA_VISION_PubCards.CardCode = CHINA_VISION_DorEvents.CardCode
WHERE (CHINA_VISION_PubCards.CardCode = '000006f1')
and CHINA_VISION_DorEvents.DorCtrls_Ref= '16' and CHINA_VISION_DorEvents.EventTM = #olddate
Order by CHINA_VISION_DorEvents.EventTM desc
Set #olddate = #date
end
close cur_mycursor
Select * from #Table order by EventTM asc
deallocate cur_mycursor