SQL View Query need time interval calculation between 2 dates - sql

I have below View Query. While calculating time interval it only display time interval if check Out is for same date. For example if i check in on 11/12/2017 then it i have to check out with same date 11/12/201 n order to calculate time Interval.
But i want to modify it like if i checkout on next day (after 12:00 AM midnight) it should also calculate the time interval.can some help me to modify query to get the desired results?
Query:
ALTER VIEW [dbo].[TimeAttendanceQuery]
AS
SELECT TOP (100) PERCENT
dbo.AxPerson.Name,
dbo.AxPerson.IdNumber AS EmployeeID,
dbo.TimeAttendance.Badge,
dbo.AxPerson.Id,
MIN(dbo.TimeAttendance.EventTime) AS EntryTime,
MAX(dbo.TimeAttendance.EventTime) AS ExitTime,
CAST(DATEDIFF(second, MIN(dbo.TimeAttendance.EventTime), MAX(dbo.TimeAttendance.EventTime)) / 60 / 60 / 24 AS NVARCHAR(50)) +
':' + CAST(DATEDIFF(second, MIN(dbo.TimeAttendance.EventTime), MAX(dbo.TimeAttendance.EventTime)) / 60 / 60 % 24 AS NVARCHAR(50)) + ':' + CAST(DATEDIFF(second, MIN(dbo.TimeAttendance.EventTime), MAX(dbo.TimeAttendance.EventTime)) / 60 % 60 AS NVARCHAR(50))
AS TimeInterval,
dbo.TimeAttendance.Event,
dbo.AxPerson.Type AS ShitType,
dbo.AxPerson.ShiftDesc,
CONVERT(Varchar,dbo.TimeAttendance.EventTime, 101) AS EventTIME
FROM
dbo.AxPerson
INNER JOIN dbo.TimeAttendance ON dbo.AxPerson.Name = dbo.TimeAttendance.Name
GROUP BY dbo.AxPerson.Name, dbo.AxPerson.IdNumber, dbo.TimeAttendance.Badge, CONVERT(Varchar, dbo.TimeAttendance.EventTime, 101), dbo. AxPerson.ShiftDesc, dbo.AxPerson.Id, dbo.TimeAttendance.Event,dbo.AxPerson.Type
ORDER BY dbo.AxPerson.Name, EventTime DESC
GO

I'm not sure about the algorithm you posted, but if you want to get the time difference from two datetimes you can cast the subtraction as time. This works for less than 24 hours. If you also want the number of days (I think this is only good for less than a year), then you can do the datepart-day of the difference.
For example:
DECLARE #starttime datetime = '2017-11-12 010:20:00'
DECLARE #endtime datetime = '2017-11-13 08:00:00'
SELECT DATEPART(DAY, #endtime - #starttime) - 1 [Days Passed]
,CAST(#endtime - #starttime as time(0)) [Time Passed]
--WHERE the (0) in time(0) is for the milliseconds to return.
Gives output:
Days Passed Time Passed
0 21:40:00
If you don't care about the days, then your code could be modified like this:
ALTER VIEW [dbo].[TimeAttendanceQuery]
AS
SELECT Name, EmployeeID, Badge, Id, EntryTime,
CAST(ExitTime - EntryTime as time(0)) [TimeInterval],
Event, ShiftType, ShiftDesc
,CONVERT(Varchar, EventTime, 101) AS EventTIME
FROM (
SELECT
dbo.AxPerson.Name,
dbo.AxPerson.IdNumber AS EmployeeID,
dbo.TimeAttendance.Badge,
dbo.AxPerson.Id,
MIN(dbo.TimeAttendance.EventTime) AS EntryTime,
MAX(dbo.TimeAttendance.EventTime) AS ExitTime,
dbo.TimeAttendance.Event,
dbo.AxPerson.Type AS ShiftType,
dbo.AxPerson.ShiftDesc,
dbo.TimeAttendance.EventTime
FROM dbo.AxPerson INNER JOIN dbo.TimeAttendance
ON dbo.AxPerson.Name = dbo.TimeAttendance.Name
GROUP BY
dbo.AxPerson.Name,
dbo.AxPerson.IdNumber,
dbo.TimeAttendance.Badge,
dbo.AxPerson.Id,
dbo.TimeAttendance.Event,
dbo.AxPerson.Type
dbo.AxPerson.ShiftDesc,
dbo.TimeAttendance.EventTime,
) AS dT

Related

Average value per minute from second

My table:
timestamp | value
------------------------+---------
2013-08-31 22:00:01.000 | 19.1
2013-08-31 22:00:03.000 | 21.5
...
Due to missing seconds in my data series i want to calculate an average value per minute. So instead of having a data series in seconds I want to have it in minutes instead, like so:
timestamp | value
-----------------+---------
2013-08-31 22:00 | 19.5
2013-08-31 22:01 | 21.1
...
How could I write an SQL query that give me this result? I am using SQL Server 2012.
Casting from datetime to smalldatetime avoids tedious (and computationally slow) mucking about with date-to-character-back-to-date conversions. The following will calculate the average per minute.
SELECT
cast(Timestamp as smalldatetime) Timestamp
,avg(value) Value
from PageLogs
group by cast(Timestamp as smalldatetime)
order by cast(Timestamp as smalldatetime)
The downsides is rounding; this would convert values between 21:00:30.000 and 22:01:29.997 to 22:00. If you need to average by "calendar" minutes (22:00:00.000 to 22:00:59.997), you'd have to adjust the times (at the millisecond level) to get the right breakpoints, like so:
SELECT
cast(dateadd(ms, -30000, Timestamp) as smalldatetime) Timestamp
,avg(value) Value
from PageLogs
group by cast(dateadd(ms, -30000, Timestamp) as smalldatetime)
order by cast(dateadd(ms, -30000, Timestamp) as smalldatetime)
You can remove seconds using CONVERT() with format 100:
SELECT CONVERT(VARCHAR,GETDATE(),100) AS Dt
,AVG(value)
FROM YourTable
GROUP BY CONVERT(VARCHAR,GETDATE(),100)
You could re-cast as DATETIME() if needed:
CAST(CONVERT(VARCHAR,GETDATE(),100)AS DATETIME)
That has seconds/miliseconds but they are all zeroes.
Goat CO's answer will work, but if you need the exact date format in your example you need to construct it manually:
select
convert(varchar, datepart(year, ts)) + '-' +
convert(varchar, datepart(month, ts)) + '-' +
convert(varchar, datepart(day, ts)) + ' ' +
convert(varchar, datepart(hour, ts)) + ':' +
convert(varchar, datepart(minute, ts)) as timestamp,
avg(value) as value
from
tbl
group by
datepart(year, ts),
datepart(month, ts),
datepart(day, ts),
datepart(hour, ts),
datepart(minute, ts)

Query to group data by 5 second time intervals

I have a table that has events that occur over a period of 1 hour.
2014-04-16 13:56:06.971 , 3474
2014-04-16 13:56:07.061 , 3609
2014-04-16 13:56:07.067 , 3617
The Table has the Time stamp and event ID
I am trying to group the data to have a count of the number of events that occurred with 5 second intervals, so it looks like this:
0-5 sec., 3
5-10 sec. , 6
10-15 sec. , 4
Thanks in Advance!
I am using SQL Server 2008
You can use a query like that:
declare #Hour datetime = '2014-04-16 13:00:00' -- starting time
select
CONVERT(VARCHAR(10), (DATEDIFF(second, #Hour, EventDateTime) / 5) * 5)
+ '-' + CONVERT(VARCHAR(10), (DATEDIFF(second, #Hour, EventDateTime) / 5 + 1) * 5) + ' sec',
COUNT(EventId)
from intervals
group by DATEDIFF(second, #Hour, EventDateTime) / 5
How about something like this, where you group by the seconds / 12
select
convert(varchar(5),[time stamp],108) as [hh:mm],
cast(min(datepart(s, [time stamp])) as varchar(10)) + ' - ' + cast(max(datepart(s, [time stamp])) as varchar(10)) as [second interval],
count(*) as total
from yourtable
group by convert(varchar(5),[time stamp],108), (datepart(s, [time stamp]) / 12)
or you could create a tally table with the start/end seconds and then join to it.

DateDiff to output hours and minutes

my code gives TOTAL HOURS in hours, but i am trying to output something like
TotalHours
8:36
where 8 represents hour part and 36 represents minutes part mean totalHours a person has worked in a single day at office.
with times as (
SELECT t1.EmplID
, t3.EmplName
, min(t1.RecTime) AS InTime
, max(t2.RecTime) AS [TimeOut]
, t1.RecDate AS [DateVisited]
FROM AtdRecord t1
INNER JOIN
AtdRecord t2
ON t1.EmplID = t2.EmplID
AND t1.RecDate = t2.RecDate
AND t1.RecTime < t2.RecTime
inner join
HrEmployee t3
ON t3.EmplID = t1.EmplID
group by
t1.EmplID
, t3.EmplName
, t1.RecDate
)
SELECT EmplID
, EmplName
, InTime
, [TimeOut]
, [DateVisited]
, DATEDIFF(Hour,InTime, [TimeOut]) TotalHours
from times
Order By EmplID, DateVisited
Very simply:
CONVERT(TIME,Date2 - Date1)
For example:
Declare #Date2 DATETIME = '2016-01-01 10:01:10.022'
Declare #Date1 DATETIME = '2016-01-01 10:00:00.000'
Select CONVERT(TIME,#Date2 - #Date1) as ElapsedTime
Yelds:
ElapsedTime
----------------
00:01:10.0233333
(1 row(s) affected)
Try this query
select
*,
Days = datediff(dd,0,DateDif),
Hours = datepart(hour,DateDif),
Minutes = datepart(minute,DateDif),
Seconds = datepart(second,DateDif),
MS = datepart(ms,DateDif)
from
(select
DateDif = EndDate-StartDate,
aa.*
from
( -- Test Data
Select
StartDate = convert(datetime,'20090213 02:44:37.923'),
EndDate = convert(datetime,'20090715 13:24:45.837')) aa
) a
Output
DateDif StartDate EndDate Days Hours Minutes Seconds MS
----------------------- ----------------------- ----------------------- ---- ----- ------- ------- ---
1900-06-02 10:40:07.913 2009-02-13 02:44:37.923 2009-07-15 13:24:45.837 152 10 40 7 913
(1 row(s) affected)
Small change like this can be done
SELECT EmplID
, EmplName
, InTime
, [TimeOut]
, [DateVisited]
, CASE WHEN minpart=0
THEN CAST(hourpart as nvarchar(200))+':00'
ELSE CAST((hourpart-1) as nvarchar(200))+':'+ CAST(minpart as nvarchar(200))END as 'total time'
FROM
(
SELECT EmplID, EmplName, InTime, [TimeOut], [DateVisited],
DATEDIFF(Hour,InTime, [TimeOut]) as hourpart,
DATEDIFF(minute,InTime, [TimeOut])%60 as minpart
from times) source
I would make your final select as:
SELECT EmplID
, EmplName
, InTime
, [TimeOut]
, [DateVisited]
, CONVERT(varchar(3),DATEDIFF(minute,InTime, TimeOut)/60) + ':' +
RIGHT('0' + CONVERT(varchar(2),DATEDIFF(minute,InTime,TimeOut)%60),2)
as TotalHours
from times
Order By EmplID, DateVisited
Any solution trying to use DATEDIFF(hour,... is bound to be complicated (if it's correct) because DATEDIFF counts transitions - DATEDIFF(hour,...09:59',...10:01') will return 1 because of the transition of the hour from 9 to 10. So I'm just using DATEDIFF on minutes.
The above can still be subtly wrong if seconds are involved (it can slightly overcount because its counting minute transitions) so if you need second or millisecond accuracy you need to adjust the DATEDIFF to use those units and then apply suitable division constants (as per the hours one above) to just return hours and minutes.
Just change the
DATEDIFF(Hour,InTime, [TimeOut]) TotalHours
part to
CONCAT((DATEDIFF(Minute,InTime,[TimeOut])/60),':',
(DATEDIFF(Minute,InTime,[TimeOut])%60)) TotalHours
The /60 gives you hours, the %60 gives you the remaining minutes, and CONCAT lets you put a colon between them.
I know it's an old question, but I came across it and thought it might help if someone else comes across it.
Divide the Datediff in MS by the number of ms in a day, cast to Datetime, and then to time:
Declare #D1 datetime = '2015-10-21 14:06:22.780', #D2 datetime = '2015-10-21 14:16:16.893'
Select Convert(time,Convert(Datetime, Datediff(ms,#d1, #d2) / 86400000.0))
If you want 08:30 ( HH:MM) format then try this,
SELECT EmplID
, EmplName
, InTime
, [TimeOut]
, [DateVisited]
, RIGHT('0' + CONVERT(varchar(3),DATEDIFF(minute,InTime, TimeOut)/60),2) + ':' +
RIGHT('0' + CONVERT(varchar(2),DATEDIFF(minute,InTime,TimeOut)%60),2)
as TotalHours from times Order By EmplID, DateVisited
Please put your related value and try this :
declare #x int, #y varchar(200),
#dt1 smalldatetime = '2014-01-21 10:00:00',
#dt2 smalldatetime = getdate()
set #x = datediff (HOUR, #dt1, #dt2)
set #y = #x * 60 - DATEDIFF(minute,#dt1, #dt2)
set #y = cast(#x as varchar(200)) + ':' + #y
Select #y
Difference Two Time in [hh:mm:ss]
select FORMAT((CONVERT(datetime,'2021-12-01 19:24:40') - CONVERT(datetime,'2021-12-01 17:00:00')),'hh:mm:ss')DffTime
this would hep you
DECLARE #DATE1 datetime = '2014-01-22 9:07:58.923'
DECLARE #DATE2 datetime = '2014-01-22 10:20:58.923'
SELECT DATEDIFF(HOUR, #DATE1,#DATE2) ,
DATEDIFF(MINUTE, #DATE1,#DATE2) - (DATEDIFF(HOUR,#DATE1,#DATE2)*60)
SELECT CAST(DATEDIFF(HOUR, #DATE1,#DATE2) AS nvarchar(200)) +
':'+ CAST(DATEDIFF(MINUTE, #DATE1,#DATE2) -
(DATEDIFF(HOUR,#DATE1,#DATE2)*60) AS nvarchar(200))
As TotalHours
Since any DateTime can be cast to a float, and the decimal part of the number represent the time itself:
DECLARE #date DATETIME = GETDATE()
SELECT CAST(CAST(#date AS FLOAT) - FLOOR(CAST(#date AS FLOAT)) AS DATETIME
This will result a datetime like '1900-01-01 hour of the day' you can cast it as time, timestamp or even use convert to get the formatted time.
I guess this works in any version of SQL since cast a datetime to float is compatible since version 2005.
Hope it helps.
In case someone is still searching for a query to display the difference in hr min and sec format:
(This will display the difference in this format: 2 hr 20 min 22 secs)
SELECT
CAST(DATEDIFF(minute, StartDateTime, EndDateTime)/ 60 as nvarchar(20)) + ' hrs ' + CAST(DATEDIFF(second, StartDateTime, EndDateTime)/60 as nvarchar(20)) + ' mins' + CAST(DATEDIFF(second, StartDateTime, EndDateTime)% 60 as nvarchar(20)) + ' secs'
OR can be in the format as in the question:
CAST(DATEDIFF(minute, StartDateTime, EndDateTime)/ 60 as nvarchar(20)) + ':' + CAST(DATEDIFF(second, StartDateTime, EndDateTime)/60 as nvarchar(20))
Sharing a variant that works for more than 24 hours.
DECLARE #sd DATETIME = CONVERT(DATETIME, '12/07/2022 11:10:00', 103)
DECLARE #ed DATETIME = CONVERT(DATETIME, '15/07/2022 13:20:05', 103)
Select Concat (
DATEDIFF(DAY, #sd, #ed), 'd ',
DATEPART(Hour, CONVERT(Time,#ed - #sd)), 'h ',
DATEPART(Minute, CONVERT(Time,#ed - #sd)), 'm ',
DATEPART(Second, CONVERT(Time,#ed - #sd)), 's'
)
Output:
3d 2h 10m 5s
For people that has MySql version < 5.6 as me they don't have TIMESTAMPDIFF so, I wrote MYTSDIFF a function that accepts %s (%m or %i) for minutes %h flags to get the difference in seconds, minutes and hours between 2 timestamps.
Enjoy!
DROP FUNCTION IF EXISTS MYTSDIFF;
DELIMITER $$
CREATE FUNCTION `MYTSDIFF`( date1 timestamp, date2 timestamp, fmt varchar(20))
returns varchar(20) DETERMINISTIC
BEGIN
declare secs smallint(2);
declare mins smallint(2);
declare hours int;
declare total real default 0;
declare str_total varchar(20);
select cast( time_format( timediff(date1, date2), '%s') as signed) into secs;
select cast( time_format( timediff(date1, date2), '%i') as signed) into mins;
select cast( time_format( timediff(date1, date2), '%H') as signed) into hours;
set total = hours * 3600 + mins * 60 + secs;
set fmt = LOWER( fmt);
if fmt = '%m' or fmt = '%i' then
set total = total / 60;
elseif fmt = '%h' then
set total = total / 3600;
else
/* Do nothing, %s is the default: */
set total = total + 0;
end if;
select cast( total as char(20)) into str_total;
return str_total;
END$$
DELIMITER ;
No need to jump through hoops. Subtracting Start from End essentially gives you the timespan
(combining Vignesh Kumar's and Carl Nitzsche's answers) :
SELECT *,
--as a time object
TotalHours = CONVERT(time, EndDate - StartDate),
--as a formatted string
TotalHoursText = CONVERT(varchar(20), EndDate - StartDate, 114)
FROM (
--some test values (across days, but OP only cares about the time, not date)
SELECT
StartDate = CONVERT(datetime,'20090213 02:44:37.923'),
EndDate = CONVERT(datetime,'20090715 13:24:45.837')
) t
Ouput
StartDate EndDate TotalHours TotalHoursText
----------------------- ----------------------- ---------------- --------------------
2009-02-13 02:44:37.923 2009-07-15 13:24:45.837 10:40:07.9130000 10:40:07:913
See the full cast and convert options here:
https://msdn.microsoft.com/en-us/library/ms187928.aspx

DATEDIFF in HH:MM:SS format

I need to calculate the total length in terms of Hours, Minutes, Seconds, and the average length, given some data with start time and end time.
For example the result must be something like 45:15:10 which means 45 hours 15 min 10 sec, or 30:07 for 30 min 07 sec.
We're using SQL Server 2008 R2 and the conversion failed when time is more than 24:59:59. Any idea of how I could do this?
For information, the columns in the table are Id, StartDateTime, EndDateTime, etc. I need to make a monthly report which contains the recordings count of the month, the total length of these records, and the average length. I'd like to know if there is an easy way to perform all of this.
You shouldn't be converting to time - it is meant to store a point in time on a single 24h clock, not a duration or interval (even one that is constrained on its own to < 24 hours, which clearly your data is not). Instead you can take the datediff in the smallest interval required (in your case, seconds), and then perform some math and string manipulation to present it in the output format you need (it might also be preferable to return the seconds to the application or report tool and have it do this work).
DECLARE #d TABLE
(
id INT IDENTITY(1,1),
StartDateTime DATETIME,
EndDateTime DATETIME
);
INSERT #d(StartDateTime, EndDateTime) VALUES
(DATEADD(DAY, -2, GETDATE()), DATEADD(MINUTE, 15, GETDATE())),
(GETDATE() , DATEADD(MINUTE, 22, GETDATE())),
(DATEADD(DAY, -1, GETDATE()), DATEADD(MINUTE, 5, GETDATE())),
(DATEADD(DAY, -4, GETDATE()), DATEADD(SECOND, 14, GETDATE()));
;WITH x AS (SELECT id, StartDateTime, EndDateTime,
d = DATEDIFF(SECOND, StartDateTime, EndDateTime),
a = AVG(DATEDIFF(SECOND, StartDateTime, EndDateTime)) OVER()
FROM #d
)
SELECT id, StartDateTime, EndDateTime,
[delta_HH:MM:SS] = CONVERT(VARCHAR(5), d/60/60)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), d/60%60), 2)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), d % 60), 2),
[avg_HH:MM:SS] = CONVERT(VARCHAR(5), a/60/60)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), a/60%60), 2)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), a % 60), 2)
FROM x;
Results:
id StartDateTime EndDateTime delta_HH:MM:SS avg_HH:MM:SS
-- ------------------- ------------------- -------------- ------------
1 2013-01-19 14:24:46 2013-01-21 14:39:46 48:15:00 42:10:33
2 2013-01-21 14:24:46 2013-01-21 14:46:46 0:22:00 42:10:33
3 2013-01-20 14:24:46 2013-01-21 14:29:46 24:05:00 42:10:33
4 2013-01-17 14:24:46 2013-01-21 14:25:00 96:00:14 42:10:33
This isn't precisely what you asked for, as it won't show just MM:SS for deltas < 1 hour. You can adjust that with a simple CASE expression:
;WITH x AS (SELECT id, StartDateTime, EndDateTime,
d = DATEDIFF(SECOND, StartDateTime, EndDateTime),
a = AVG(DATEDIFF(SECOND, StartDateTime, EndDateTime)) OVER()
FROM #d
)
SELECT id, StartDateTime, EndDateTime,
[delta_HH:MM:SS] = CASE WHEN d >= 3600 THEN
CONVERT(VARCHAR(5), d/60/60) + ':' ELSE '' END
+ RIGHT('0' + CONVERT(VARCHAR(2), d/60%60), 2)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), d % 60), 2),
[avg_HH:MM:SS] = CASE WHEN a >= 3600 THEN
CONVERT(VARCHAR(5), a/60/60) + ':' ELSE '' END
+ RIGHT('0' + CONVERT(VARCHAR(2), a/60%60), 2)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), a % 60), 2)
FROM x;
This query changes the delta column in the 2nd row in the above result from 0:22:00 to 22:00.
I slightly modified Avinash's answer as it may end with error if difference is too big. If you need only HH:mm:ss it is sufficient to distinguish at seconds level ony like this:
SELECT CONVERT(time,
DATEADD(s,
DATEDIFF(s,
'2018-01-07 09:53:00',
'2018-01-07 11:53:01'),
CAST('1900-01-01 00:00:00.0000000' as datetime2)
)
)
SELECT CONVERT(time,
DATEADD(mcs,
DATEDIFF(mcs,
'2007-05-07 09:53:00.0273335',
'2007-05-07 09:53:01.0376635'),
CAST('1900-01-01 00:00:00.0000000' as datetime2)
)
)
If you want to do averages, then the best approach is to convert to seconds or fractions of a day. Day fractions are convenient in SQL Server, because you can do things like:
select avg(cast(endtime - starttime) as float)
from t
You can convert it back to a datetime using the reverse cast:
select cast(avg(cast(endtime - starttime as float) as datetime)
from t
The arithmetic to get the times in the format you want . . . that is a pain. You might consider including days in the final format, and using:
select right(convert(varchar(255), <val>, 120), 10)
To get the hours exceeding 24, here is another approach:
select cast(floor(cast(<val> as float)*24) as varchar(255))+right(convert(varchar(255), <val>, 120), 6)
It uses convert for minutes and seconds, which should be padded with 0s on the left. It then appends the hours as a separate value.
Starting in SQL SERVER 2012, you don't need to use DATEDIFF function. You can use FORMAT function to achieve what you want:
SELECT
FORMAT(CONVERT(TIME, [appoitment].[Start] - [appointment].[End]), N'hh\:mm') AS 'Duration'
FROM
[tblAppointment] (NOLOCK)
A way that avoids overflows and can include days and go all the way to milliseconds in the output:
DECLARE #startDate AS DATETIME = '2018-06-01 14:20:02.100'
DECLARE #endDate AS DATETIME = '2018-06-02 15:23:09.000'
SELECT CAST(DATEDIFF(day,'1900-01-01', #endDate - #startDate) AS VARCHAR) + 'd ' + CONVERT(varchar(22), #endDate - #startDate, 114)
The above will return
1d 01:03:06:900
And, off course, you can use the formatting of your choice
SQL Supports datetime substraction which outputs a new datetime relative to the MIN date (for instance 1900-01-01, you can probably get this value from some system variable) This works better than DATEDIFF, because DATEDIFF will count ONE for each "datepart boundaries crossed", even if the elapsed time is less than a whole datapart. Another nice thing about this method is that it allows you to use the date formatting conversions.
If days is the (positive) number of days, like 0.5 for 12 hours, use this expression to format it as a proper duration:
CONVERT(varchar(9), FLOOR(days * 24)) + RIGHT(CONVERT(char(19), CAST(days AS datetime), 120), 6)
Excel will understands values up to 9999:59:59 when pasted. There apply a custom format: [h]:mm:ss in the English version ([u]:mm:ss for Dutch).

How can I group time by hour or by 10 minutes?

Like when I do
SELECT [Date]
FROM [FRIIB].[dbo].[ArchiveAnalog]
GROUP BY [Date]
How can I specify the group period? I'm using MS SQL 2008.
I've tried this, both with % 10 and / 10.
SELECT MIN([Date]) AS RecT, AVG(Value)
FROM [FRIIB].[dbo].[ArchiveAnalog]
GROUP BY (DATEPART(MINUTE, [Date]) / 10)
ORDER BY RecT
Is it possible to make Date output without milliseconds?
finally done with
GROUP BY
DATEPART(YEAR, DT.[Date]),
DATEPART(MONTH, DT.[Date]),
DATEPART(DAY, DT.[Date]),
DATEPART(HOUR, DT.[Date]),
(DATEPART(MINUTE, DT.[Date]) / 10)
Short and sweet
GROUP BY DATEDIFF(MINUTE, '2000', date_column) / 10
With heavy acknowledgements to Derek's answer, which forms the core of this one.
Practical usage
SELECT DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', aa.[date]) / 10 * 10, '2000')
AS [date_truncated],
COUNT(*) AS [records_in_interval],
AVG(aa.[value]) AS [average_value]
FROM [friib].[dbo].[archive_analog] AS aa
-- WHERE aa.[date] > '1900-01-01'
GROUP BY DATEDIFF(MINUTE, '2000', aa.[date]) / 10
-- HAVING SUM(aa.[value]) > 1000
ORDER BY [date_truncated]
Details and commentary
The MINUTE and 10 terms can be changed to any DATEPART and integer,1 respectively, to group into different time intervals.
e.g. 10 with MINUTE is ten minute intervals; 6 with HOUR is
six hour intervals.
If you change the interval a lot, you might benefit from declaring it as a variable.
DECLARE #interval int = 10;
SELECT DATEADD(MINUTE, DATEDIFF(…) / #interval * #interval, '2000')
…
GROUP BY DATEDIFF(…) / #interval
Wrapping it with a DATEADD invocation with a multiplier will give you a DATETIME value, which means:
Data sources over long time intervals are fine. Some other answers have collision between years.
Including it in the SELECT statement will give your output a single column with the truncated timestamp.
In the SELECT, the division (/) operation after DATEDIFF truncates values to integers (a FLOOR shortcut), which yields the beginning of time intervals for each row.
If you want to label each row with the middle or end of its interval, you can tweak the division in the second term of DATEADD with the bold part below:
End of interval: …) / 10 * 10 + 10 , '2000'), credit to Daniel Elkington.
Middle of interval: …) / 10 * 10 + (10 / 2.0) , '2000').
Trivia
'2000' is an "anchor date" around which SQL will perform the date math. Most sample code uses 0 for the anchor, but JereonH discovered that you encounter an integer overflow when grouping more-recent dates by seconds or milliseconds.2
If your data spans centuries,3 using a single anchor date in the GROUP BY for seconds or milliseconds will still encounter the overflow. For those queries, you can ask each row to anchor the binning comparison to its own date's midnight:
Use DATEADD(DAY, DATEDIFF(DAY, 0, aa.[date]), 0) instead of '2000' wherever it appears above. Your query will be totally unreadable, but it will work.
An alternative might be CONVERT(DATETIME, CONVERT(DATE, aa.[date])) as the replacement.
1 If you want all :00 timestamps to be eligible for binning, use an integer that your DATEPART's maximum can evenly divide into.4 As a counterexample, grouping results into 13-minute or 37-hour bins will skip some :00s, but it should still work fine.
2 The math says 232 ≈ 4.29E+9. This means for a DATEPART of SECOND, you get 4.3 billion seconds on either side, which works out to "anchor date ± 136 years." Similarly, 232 milliseconds is ≈ 49.7 days.
3 If your data actually spans centuries or millenia and is still accurate to the second or millisecond… congratulations! Whatever you're doing, keep doing it.
4 If you ever wondered why our clocks have a 12 at the top, reflect on how 5 is the only integer from 6 (half of 12) or below that is not a factor of 12. Then note that 5 × 12 = 60. You have lots of choices for bin sizes with hours, minutes, and seconds.
In T-SQL you can:
SELECT [Date]
FROM [FRIIB].[dbo].[ArchiveAnalog]
GROUP BY [Date], DATEPART(hh, [Date])
or
by minute use DATEPART(mi, [Date])
or
by 10 minutes use DATEPART(mi, [Date]) / 10 (like Timothy suggested)
For a 10 minute interval, you would
GROUP BY (DATEPART(MINUTE, [Date]) / 10)
As was already mentioned by tzup and Pieter888... to do an hour interval, just
GROUP BY DATEPART(HOUR, [Date])
Should be something like
select timeslot, count(*)
from
(
select datepart('hh', date) timeslot
FROM [FRIIB].[dbo].[ArchiveAnalog]
)
group by timeslot
(Not 100% sure about the syntax - I'm more an Oracle kind of guy)
In Oracle:
SELECT timeslot, COUNT(*)
FROM
(
SELECT to_char(l_time, 'YYYY-MM-DD hh24') timeslot
FROM
(
SELECT l_time FROM mytab
)
) GROUP BY timeslot
The original answer the author gave works pretty well. Just to extend this idea, you can do something like
group by datediff(minute, 0, [Date])/10
which will allow you to group by a longer period then 60 minutes, say 720, which is half a day etc.
For MySql:
GROUP BY
DATE(`your_date_field`),
HOUR(`your_date_field`),
FLOOR( MINUTE(`your_date_field`) / 10);
If you want to actually display the date, have a variable grouping, and be able to specify larger time frames than 60 minutes:
DECLARE #minutes int
SET #minutes = 90
SELECT
DATEADD(MINUTE, DATEDIFF(MINUTE, 0, [Date]) / #minutes * #minutes, 0) as [Date],
AVG([Value]) as [Value]
FROM [FRIIB].[dbo].[ArchiveAnalog]
GROUP BY
DATEDIFF(MINUTE, 0, [Date]) / #minutes
declare #interval tinyint
set #interval = 30
select dateadd(minute,(datediff(minute,0,[DateInsert])/#interval)*#interval,0), sum(Value_Transaction)
from Transactions
group by dateadd(minute,(datediff(minute,0,[DateInsert])/#interval)*#interval,0)
In SQLite, in order to group by hour, you can do:
GROUP BY strftime('%H', [FRIIB].[dbo].[ArchiveAnalog].[Date]);
and to group by each 10 minutes:
GROUP BY strftime('%M', [FRIIB].[dbo].[ArchiveAnalog].[Date]) / 10;
My solution is to use a function to create a table with the date intervals and then join this table to the data I want to group using the date interval in the table.
The date interval can then be easily selected when presenting the data.
CREATE FUNCTION [dbo].[fn_MinuteIntervals]
(
#startDate SMALLDATETIME ,
#endDate SMALLDATETIME ,
#interval INT = 1
)
RETURNS #returnDates TABLE
(
[date] SMALLDATETIME PRIMARY KEY NOT NULL
)
AS
BEGIN
DECLARE #counter SMALLDATETIME
SET #counter = #startDate
WHILE #counter <= #endDate
BEGIN
INSERT INTO #returnDates VALUES ( #counter )
SET #counter = DATEADD(n, #interval, #counter)
END
RETURN
END
For SQL Server 2012, though I believe it would work in SQL Server 2008R2, I use the following approach to get time slicing down to the millisecond:
DATEADD(MILLISECOND, -DATEDIFF(MILLISECOND, CAST(time AS DATE), time) % #msPerSlice, time)
This works by:
Getting the number of milliseconds between a fixed point and target time:#ms = DATEDIFF(MILLISECOND, CAST(time AS DATE), time)
Taking the remainder of dividing those milliseconds into time slices:#rms = #ms % #msPerSlice
Adding the negative of that remainder to the target time to get the slice time:DATEADD(MILLISECOND, -#rms, time)
Unfortunately, as is this overflows with microseconds and smaller units, so larger, finer data sets would need to use a less convenient fixed point.
I have not rigorously benchmarked this and I am not in big data, so your mileage may vary, but performance was not noticeably worse than the other methods tried on our equipment and data sets, and the payout in developer convenience for arbitrary slicing makes it worthwhile for us.
select dateadd(minute, datediff(minute, 0, Date), 0),
sum(SnapShotValue)
FROM [FRIIB].[dbo].[ArchiveAnalog]
group by dateadd(minute, datediff(minute, 0, Date), 0)
select from_unixtime( 600 * ( unix_timestamp( [Date] ) % 600 ) ) AS RecT, avg(Value)
from [FRIIB].[dbo].[ArchiveAnalog]
group by RecT
order by RecT;
replace the two 600 by any number of seconds you want to group.
If you need this often and the table doesn't change, as the name Archive suggests, it would probably be a bit faster to convert and store the date (& time) as a unixtime in the table.
I know I am late to the show with this one, but I used this - pretty simple approach. This allows you to get the 60 minute slices without any rounding issues.
Select
CONCAT(
Format(endtime,'yyyy-MM-dd_HH:'),
LEFT(Format(endtime,'mm'),1),
'0'
) as [Time-Slice]
Try this query. It makes one column. (references #nobilist answer)
GROUP BY CAST(DATE(`your_date_field`) as varchar) || ' ' || CAST(HOUR(`your_date_field`) as varchar) || ':' || CAST(FLOOR(minute(`your_date_field`) / 10) AS varchar) || '0' AS date_format
Here is an option that provides a human readable start time of that interval (7:30, 7:40, etc).
In a temp table, it truncates seconds and milliseconds by using SMALLDATETIME, and then the main query subtracts any amount over the desired minute interval.
SELECT DATEADD(MINUTE, -(DATEDIFF(MINUTE, '2000', tmp.dt) % 10), tmp.dt)
FROM (
SELECT CAST(DateField AS SMALLDATETIME) AS dt
FROM MyDataTable
) tmp
It can also be done in a single line of code, but it is not as readable.
SELECT DATEADD(MINUTE, -(DATEDIFF(MINUTE, '2000', CAST(DateField AS SMALLDATETIME)) % 10), CAST(DateField AS SMALLDATETIME)) AS [interval] FROM MyDataTable