SQL get time values between a time range - sql

I have to calculate all the time values which are between a particular start time and end time on the basis of a particular time interval on a date in stored procedure in SQL.
I have 4 parameters for my stored procedure.
#date_value nvarchar(1000),//date value'2016-10-09'
#starttime TIME(0) //= '08:00',//suppose starttime is 8am
#endtime TIME(0) //= '13:00'
#interval INT //= '20' //20 minutes
I am trying to get all the time values between the starttime and endtime. I need it like this.
8:00,8:20,08:40........upto 12:50(end value I dont need).
I have googled it and found that we can use
SELECT DATEADD(MINUTE, #MinutesToAdd, #StartTime)
Here I am not able to include the end time.Please help me

Declare
#date_value nvarchar(1000)='2016-10-09',
#starttime TIME(0)= '08:00',
#endtime TIME(0) = '13:00',
#interval INT = '20'
;With cte(stime)
as
(
SELECT
cast(cast( #date_value as datetime)
+ CONVERT(CHAR(8), #starttime, 108) as time)
union all
select
cast(dateadd(minute,#interval,stime) as time)
from cte
where cast(dateadd(minute,#interval,stime) as time)<#endtime
)
select * from cte

Here is one way without LOOP or Recursion
;with cte as
(
SELECT ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n as seq
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) thousands(n)
),time_gen as
(
SELECT Dateadd(MINUTE, #interval * seq , Cast(Concat(#date_value, ' ', #starttime) AS DATETIME)) as dates
FROM cte
)
select cast(dates as time) times from time_gen
WHERE dates < Cast(Concat(#date_value, ' ', #endtime) AS DATETIME)
order by times
Note : If you not using SQL SERVER 2012+, then use + for concatenation instead of Concat function

DECLARE #date_value nvarchar(1000) = '2016-10-09'
DECLARE #starttime TIME(0) = '08:00'
DECLARE #endtime TIME(0) = '13:00'
DECLARE #interval INT = '20'
DECLARE #IterateTime AS TIME(0) = #starttime
DECLARE #Diff AS INT = (SELECT DATEDIFF(MINUTE, #starttime, #endtime)) / #interval
DECLARE #Iterator AS INT = 0
WHILE #Iterator < #Diff
BEGIN
SELECT #IterateTime = DATEADD(MINUTE, #interval, #IterateTime)
SELECT #Iterator = #Iterator + 1
SELECT #IterateTime
END

Related

SQL Query, bring data between 2 dates with limitation

In SQL, I am going to write a query which insert all data between 2 dates and also I want to bring then in a 1000 batch but since the number of data between those days are more than my limitation I was going to write a loop which makes the smaller period to bring the data.
here is my code:
DECLARE #StartDate DATETIME = CAST('2021-06-02 01:00:00.000' AS DATETIME)
DECLARE #EndDate DATETIME = CAST('2021-06-23 01:00:00.000' AS DATETIME)
DECLARE #RealRowCount INT = (SELECT DISTINCT SUM(##ROWCOUNT) OVER() FROM GetReport (
#StartDate, #EndDate))
DECLARE #TransactionCount INT = (SELECT DISTINCT TransactionCount FROM GetReport (
#StartDate, #EndDate))
WHILE #RealRowCount < #TransactionCount
BEGIN
DECLARE #DiffDate INT = (SELECT DATEDIFF(DAY, #StartDate, #EndDate))
SET #EndDate = DATEADD(DAY, #DiffDate/2 ,#StartDate)
SELECT *,#StartDate, #EndDate FROM GetReport (#StartDate, #EndDate)
END
PS: I was thinking about find the middle of the period of date and then change them into the new EneDate and StartDate but there is problem here!
Your question is not very clear. Suppose you have 10,000 records between two dates and you do not want to retrieve more than a thousand records at a time. In this case, you can use pagination. Both in the program code and in SQL.
DECLARE #StartDate DATETIME = CAST('2021-06-02 01:00:00.000' AS DATETIME)
DECLARE #EndDate DATETIME = CAST('2021-06-23 01:00:00.000' AS DATETIME)
DECLARE #RealRowCount INT = (SELECT DISTINCT COUNT(*) FROM Products WHERE InsertDate BETWEEN #StartDate AND #EndDate)
DECLARE #Counter INT = 0
WHILE #Counter <= #RealRowCount
BEGIN
SELECT *
FROM Products
WHERE InsertDate BETWEEN #StartDate AND #EndDate
ORDER BY InsertDate
OFFSET #Counter ROWS -- skip #Counter rows
FETCH NEXT 1000 ROWS ONLY -- take 1000 rows
SET #Counter = #Counter + 1000
END
Or you can get the time difference between the two dates and add the start date each time in a specific step and retrieve the data of that date.
For example, the date difference is 20 days. Increase the start date by 5 steps each time to the start date with the end date
I create another table to put Dates and if this table has any rows I can get 'EndDate', but if it has not any records I simply just use the date that I specified.
AccSync is the table that I insert details of my records and AccTransformation is the table wich I want to insert all of my records.
DECLARE #Count INT = (SELECT COUNT(*) FROM [AccTransaction])
DECLARE #Flag BIT = (SELECT IIF(#Count > 1, 1, 0))
DECLARE #End DATETIME = GETDATE();
DECLARE #Start DATETIME
IF(#Flag = 0)
BEGIN
SET #Start = CAST('2021-03-08' AS DATETIME2);
SET #Flag = 1
END
ELSE IF(#Flag = 1)
BEGIN
SET #Start = (SELECT TOP 1 EndDate FROM (SELECT EndDate FROM [AccSync] ORDER BY ActionDate DESC OFFSET 0 ROW) AS TT);
END
DECLARE #RealRowCount INT = (SELECT DISTINCT SUM(##ROWCOUNT) FROM [GetReport] (#Start, #End));
DECLARE #TransactionCount INT = (SELECT DISTINCT TransactionCount FROM [GetReport] (#Start, #End));
----------------------------------------------------------------------------------------------
WHILE (#RealRowCount <> #TransactionCount)
BEGIN
DECLARE #DiffDate INT = (SELECT DATEDIFF(SECOND, #Start, #End))
SET #End = DATEADD(SECOND, (#DiffDate/2), #Start)
SET #RealRowCount = (SELECT DISTINCT SUM(##ROWCOUNT) FROM [GetReport] (#Start, #End))
SET #TransactionCount = (SELECT DISTINCT TransactionCount FROM [GetReport] (#Start, #End))
END
----------------------------------------------------------------------------------------------
INSERT INTO [AccTransaction]
SELECT *
FROM [GetReport](#Start, #End)
----------------------------------------------------------------------------------------------
INSERT INTO [AccSync]
VALUES(NEWID(), GETDATE(), #Start, #End, ISNULL(#TransactionCount,0), DATEDIFF(SECOND, #Start, #End))

Getting Minutes by Hour for Date Range

I'm trying to write a SQL query (SQL Server) and part of it is determining the number of minutes per hour between two datetimes.
Example: 11/1/2018 09:05 - 11/1/2018 13:15
Hour 09: 55 minutes
Hour 10: 60 minutes
Hour 11: 60 minutes
Hour 12: 60 minutes
Hour 13: 15 minutes
These would then get put into a temp table and grouped by some other data which will then be used to calculate dollar amounts from these minutes.
Is there a way to accomplish something like this via SQL that isn't too slow or laborious?
Thanks!
I think a recursive CTE is possibly the best approach:
with cte as (
select startTime, endTime,
startTime_hour as hourStart,
(case when endTime < dateadd(hour, 1, startTime_hour) then endTime
else dateadd(hour, 1, startTime_hour)
end) as hourEnd
from (select t.*,
dateadd(hour, datediff(hour, 0, startTime), 0) as startTime_hour
from t
) t
union all
select startTime, endTime,
dateadd(hour, 1, hourStart) as hourStart,
(case when endTime < dateadd(hour, 2, hourStart) then endTime
else dateadd(hour, 2, hourStart)
end) as endHour
from cte
where hourEnd < endTime
)
select cte.hourStart,
(case when hourStart > startTime then datediff(minute, hourStart, hourEnd) else datediff(minute, startTime, hourEnd) end) as minutes
from cte
order by hourStart;
Here is a db<>fiddle.
Here is an alternative dynamic solution that you can work with two parameters (start/end dates) only:
create table #temp
([hour] int, [minutes] int)
declare #startTime datetime='11/1/2018 09:05'
declare #EndTime datetime='11/1/2018 13:15'
declare #tempStartTime datetime = #startTime
declare #nextTimeRounded datetime
declare #hourdiff int = DATEDIFF(HOUR,#startTime,#EndTime)
declare #counter int = DATEPART(HH,#startTime)
declare #limit int = #counter + #hourdiff + 1
while #counter < #limit
begin
insert into #temp ([hour]) values (#counter)
set #nextTimeRounded= (dateadd(hour,
1 + datepart(hour, #tempStartTime),
cast(convert(varchar(10),#tempStartTime, 112) as datetime))
)
if #nextTimeRounded > #EndTime
begin
set #nextTimeRounded = #EndTime
end
update #temp
set [minutes] = (case when DATEDIFF(MINUTE,#tempStartTime,#nextTimeRounded)=0 then 60 else DATEDIFF(MINUTE,#tempStartTime,#nextTimeRounded) end)
where [hour] = #counter
set #counter = #counter + 1
set #tempStartTime = DATEADD(MINUTE,DATEDIFF(MINUTE,#tempStartTime,#nextTimeRounded),#tempStartTime);
end
select * from #temp
Sample Data
Below, we pump four time ranges, with associated values, into a table. All time ranges are different, but the first two are 10h 30m apart. The second two are 9h 45m apart.
declare #times table (
startTime time,
endTime time,
val float
);
insert #times values
('2018-10-01 01:00:00', '2018-10-01 10:45:00', 7),
('2018-10-02 01:00:00', '2018-10-02 10:45:00', 8),
('2018-10-01 01:00:00', '2018-10-01 11:30:00', 1),
('2018-10-02 01:00:00', '2018-10-02 11:30:00', 3);
Solution
You can use the 'datediff' function to aggregate as you so desire. Use the modulo operator to convert your minutes into just the minutes that remain over when whole hours are discounted.
select ap.h,
ap.m,
sumVal = sum(val)
from #times
cross apply (select
h = datediff(hour, startTime, endTime),
m = datediff(minute, startTime, endTime) % 60
) ap
group by ap.h,
ap.m

Returning Valid Time Slots with a Given Interval

I am trying to return all the valid time slots between a start and end time with a given interval via SQL, but am having a mental block big time. Below is some sample code with some variables and then a table to store each value. The variables are using INT, as that is the data type where the values will eventually be stored is INT, and can't be changed
DECLARE #StartTime INT = 900
DECLARE #EndTime INT = 2000
DECLARE #CurrentTime INT = 900
DECLARE #Interval INT = 15
DECLARE #Times TABLE
(
[Time] INT
)
WHILE (#CurrentTime <= #EndTime)
BEGIN
INSERT INTO #Times VALUES (#CurrentTime)
SET #CurrentTime = #CurrentTime + #Interval
END
When the above runs it populates, but gives me invalid values. Values returned:
900
915
930
945
960
975
etc.
The desired results are:
900
915
930
945
1000
1015
1030
etc.
Is there some way to do this, by dividing the current value and expecting a remainder, or an easy way to achieve the desired results?
Thanks
Try this
DECLARE #StartTime INT = 900
DECLARE #EndTime INT = 2000
DECLARE #CurrentTime INT = 900
DECLARE #Interval INT = 15
DECLARE #Times TABLE
(
[Time] INT
)
WHILE (#CurrentTime <= #EndTime)
BEGIN
INSERT INTO #Times VALUES (#CurrentTime)
SET #CurrentTime = #CurrentTime + #Interval
IF(RIGHT(#CurrentTime,2) = 60)
SET #CurrentTime = #CurrentTime + 40
END
SELECT * FROM #Times
This is a bit more complex, but has several differences over the above answer: it can bridge midnight, handle intervals that aren't factors of 60 (try the above with an interval of 7), and uses a recursive CTE to generate the numbers instead of a WHILE loop.
Its a shame about the INT datatype, with all the bonuses of TSQL's TIME datatype. You could do away with the helper functions.
The MAXRECURSION option is sometimes problematic, as you can't use it inside of a view... the calling statement has to specify it. To get around this, wrap it in a multi-statement table-valued function. see this post
CREATE FUNCTION dbo.ConvertIntToTime (#TimeAsInt INT)
RETURNS TIME
BEGIN
DECLARE #TimeString VARCHAR(4) = RIGHT(REPLICATE('0',4) + CAST(#TimeAsInt AS VARCHAR(4)),4);
RETURN PARSE(LEFT(#TimeString,2) + ':' + RIGHT(#TimeString,2) AS TIME);
END;
GO
CREATE FUNCTION dbo.ConvertTimeToInt (#Time TIME)
RETURNS INT
BEGIN
DECLARE #TimeString VARCHAR(5) = CONVERT(VARCHAR(5), #Time, 108);
RETURN PARSE(LEFT(#TimeString,2) + RIGHT(#TimeString,2) AS INT);
END;
GO
DECLARE #CurrentTime AS INT = 900;
DECLARE #EndTime AS INT = 1700;
DECLARE #Interval AS INT = 10;
DECLARE #CurrentTimeAsTime TIME = dbo.ConvertIntToTime(#CurrentTime);
DECLARE #EndTimeAsTime TIME = dbo.ConvertIntToTime(#EndTime);
WITH CTEGenerateTimes AS
(
SELECT #CurrentTimeAsTime Time
UNION ALL
SELECT DATEADD(MINUTE, #Interval, Time) AS TIME
FROM CTEGenerateTimes
WHERE (#EndTimeAsTime >= #CurrentTimeAsTime AND DATEADD(MINUTE, #Interval, Time) <= #EndTimeAsTime AND DATEADD(MINUTE, #Interval, Time) <> CAST('00:00' AS TIME)) --Range doesn't cross midnight
OR (#EndTimeAsTime < #CurrentTimeAsTime AND DATEADD(MINUTE, #Interval, Time) > #CurrentTimeAsTime AND DATEADD(MINUTE, #Interval, Time) <= CAST('23:59:59' AS TIME)) --Range crosses midnight, portion before midnight
OR (#EndTimeAsTime < #CurrentTimeAsTime AND DATEADD(MINUTE, #Interval, Time) <= #CurrentTimeAsTime AND DATEADD(MINUTE, #Interval, Time) <= #EndTimeAsTime)
)
SELECT dbo.ConvertTimeToInt(t.Time) Time
FROM CTEGenerateTimes t
OPTION (MAXRECURSION 1441)

I want Hours,Min, second difference from two datetime

I am developing Time management system for employees.
I want the duration how much duration employee come late , or he went early.
i have following structure.
**Attendace**
AutoId --uniqueidentifier
EMployeeId --uniqueidentifier
Date --datetime
InTime -- varchar(50)
OutTime -- varchar(50)
ActualInTime--datetime
ActualOutTime--datetime
I want Late Coming Report ( i.e. who came late in morning after ActualInTime and how much duration in hh:mm:ss ) and also want early going(i.e who went early in the evening before ActualOutTime in duration in format hh:mm:ss )
So can you please help me..???
I came across an easier way of solving this issue.
First, a quick example of turning a "number of seconds" into the "hh:mm:ss" format.
DECLARE #NumberOfSeconds int
SET #NumberOfSeconds = 3843 -- 1 hour, 4 minutes, 3 seconds
SELECT #NumberOfSeconds AS 'seconds',
CONVERT(varchar, DATEADD(second, #NumberOfSeconds, 0), 108) AS 'hh:mm:ss'
This will give us this output:
And we can easily take this a step further, calculate the number of seconds between two datetimes, and display it in hh:mm:ss format:
DECLARE
#NumberOfSeconds int,
#StartTime datetime = '2017-09-14 14:16:11',
#EndTime datetime = '2017-09-14 14:23:13'
SET #NumberOfSeconds = DATEDIFF(second, #StartTime, #EndTime)
SELECT #NumberOfSeconds AS 'seconds',
CONVERT(varchar, DATEADD(second, #NumberOfSeconds, 0), 108) AS 'hh:mm:ss'
Which gives us this output:
Simple, hey ?
(And yes, you can simplify it further by putting the DATEDIFF directly into the DATEADD function.)
You can do it in a very simple way:
declare #date1 datetime, #date2 datetime
set #date1=DATEADD(s,-638,getdate())
set #date2=GETDATE()
select convert(char(8),dateadd(s,datediff(s,#date1,#date2),'1900-1-1'),8)
... the result is 00:10:38 (638s = 600s + 38s = 10 minutes and 38 seconds)
Another example:
select distinct convert(char(8),dateadd(s,datediff(s, CRDATE , GETDATE() ),'1900-1-1'),8) from sysobjects order by 1
It will works until the difference of 86399 seconds (23:59:59):
select convert(char(8),dateadd(s,datediff(s
, DATEADD(s,-86399,GETDATE())
, GETDATE()
),'1900-1-1'),8)
... after that it will return to zero:
select convert(char(8),dateadd(s,datediff(s
, DATEADD(s,-86400,GETDATE())
, GETDATE()
),'1900-1-1'),8)
Because they are the same day (you don't have to worry about number of hours >24), you can just use a combination of DATEDIFF(second,time1,time2) and DATEADD(second,0,) to get a datetime value.
To format to hh:nn:ss, use convert(char(8),answer,8) but this is something better done by the reporting front end against the datetime result.
-- Late report
select *, dateadd(s,0,datediff(s,intime,actualintime)) late_by
from attendance
where intime < actualintime
this code might help you...
DECLARE #First datetime
DECLARE #Second datetime
SET #First = '04/02/2008 05:23:22'
SET #Second = getdate()
SELECT DATEDIFF(day,#First,#Second)*24 as TotalHours,
DATEDIFF(day,#First,#Second)*24*60 as TotalMinutes,
DATEDIFF(day,#First,#Second)*24*60*60 as TotalSeconds
well, yes, you need to use DATEDIFF, and yes, all that posted above works, but, if you want to show 07:07:07 instead of 7:7:7, you have to do something like this:
Declare #starttime datetime, #endtime datetime, #seconds int, #minutes int, #hours int
Set #starttime ='2013-10-01 05:05:17'
Set #endtime = '2013-10-01 23:10:18'
set #hours = DateDiff(hour, #starttime, #endtime)
set #minutes = DateDiff(minute, #starttime, #endtime);
set #seconds = DateDiff(second, #starttime, #endtime);
select case when DateDiff(minute, #starttime, #endtime) > 60
then CASE WHEN #hours >= 10 THEN cast(#hours as varchar(3))
ELSE '0' + cast(#hours as varchar(3)) END +':' +
CASE WHEN #minutes - (#hours * 60) >= 10 THEN
cast((#minutes - (#hours * 60)) as varchar(3))
ELSE '0' +cast((#minutes - (#hours * 60)) as varchar(3)) END
+ CASE WHEN (#seconds - (#minutes *60)) >= 10 THEN
+':' + cast(#seconds - (#minutes *60) as varchar(10))
ELSE ':0' + cast(#seconds - (#minutes *60) as varchar(10)) END
ELSE '0' + cast(#minutes as varchar(3)) +':' + cast(#seconds as varchar(10))
end
It may not look very nice, but it gave me what i wanted.
How about using CAST.
,CAST (Table1.DateTimeLatest-Table1.DateTimeFirst as time) as [Elapsed Time]
The raw result from SSMS from an apparatus table:
SQL Return shows out to nanoseconds in this Data.
For the report, as pasted in formatted Excel sheet:
Formatted result column as hh:mm:ss.
SELECT id, pickupdateandtime, GETDATE() AS CurrentTime,
((DATEDIFF(day,GETDATE(),pickupdateandtime)) - 1) AS Days ,
convert(char(8),dateadd(s,datediff(s,GETDATE(),pickupdateandtime),'1900-1-
1'),8) AS 'Hours & Mins' FROM orders
Here's what worked for me. Thank you #lynx_74.
https://i.stack.imgur.com/hOmyJ.png
Create a stored procedure to do the work and then just call the procedure passing your start and end dates.
CREATE PROCEDURE [dbo].[GetOperationDuration]
#DurationStart DATETIME, #DurationEnd DATETIME,
#Duration VARCHAR(100) OUTPUT
AS
BEGIN
DECLARE #years INT, #months INT, #days INT,
#hours INT, #minutes INT, #seconds INT, #milliseconds INT;
-- DOES NOT ACCOUNT FOR LEAP YEARS
SELECT #years = DATEDIFF(yy, #DurationStart, #DurationEnd)
IF DATEADD(yy, -#years, #DurationEnd) < #DurationStart
SELECT #years = #years-1
SET #DurationEnd = DATEADD(yy, -#years, #DurationEnd)
SELECT #months = DATEDIFF(mm, #DurationStart, #DurationEnd)
IF DATEADD(mm, -#months, #DurationEnd) < #DurationStart
SELECT #months=#months-1
SET #DurationEnd= DATEADD(mm, -#months, #DurationEnd)
SELECT #days=DATEDIFF(dd, #DurationStart, #DurationEnd)
IF DATEADD(dd, -#days, #DurationEnd) < #DurationStart
SELECT #days=#days-1
SET #DurationEnd= DATEADD(dd, -#days, #DurationEnd)
SELECT #hours=DATEDIFF(hh, #DurationStart, #DurationEnd)
IF DATEADD(hh, -#hours, #DurationEnd) < #DurationStart
SELECT #hours=#hours-1
SET #DurationEnd= DATEADD(hh, -#hours, #DurationEnd)
SELECT #minutes=DATEDIFF(mi, #DurationStart, #DurationEnd)
IF DATEADD(mi, -#minutes, #DurationEnd) < #DurationStart
SELECT #minutes=#minutes-1
SET #DurationEnd= DATEADD(mi, -#minutes, #DurationEnd)
SELECT #seconds=DATEDIFF(s, #DurationStart, #DurationEnd)
IF DATEADD(s, -#seconds, #DurationEnd) < #DurationStart
SELECT #seconds=#seconds-1
SET #DurationEnd= DATEADD(s, -#seconds, #DurationEnd)
SELECT #milliseconds=DATEDIFF(ms, #DurationStart, #DurationEnd)
SELECT #Duration= ISNULL(CAST(NULLIF(#years,0) AS VARCHAR(10)) + ' years,','')
+ ISNULL(' ' + CAST(NULLIF(#months,0) AS VARCHAR(10)) + ' months,','')
+ ISNULL(' ' + CAST(NULLIF(#days,0) AS VARCHAR(10)) + ' days,','')
+ ISNULL(' ' + CAST(NULLIF(#hours,0) AS VARCHAR(10)) + ' hours,','')
+ ISNULL(' ' + CAST(#minutes AS VARCHAR(10)) + ' minutes and','')
+ ISNULL(' ' + CAST(#seconds AS VARCHAR(10))
-- UNCOMMENT THEFOLLOWING IF YOU WANT MILLISECONDS INCLUDED
--+ CASE
--WHEN #milliseconds > 0
--THEN '.' + CAST(#milliseconds AS VARCHAR(10))
--ELSE ''
--END
+ ' seconds','')
SELECT #Duration
END
GO
Then just call using:
DECLARE #return_value int, #Duration varchar(100)
EXEC #return_value = [dbo].[GetOperationDuration] #DurationStart, #DurationEnd, #Duration = #Duration OUTPUT
SELECT #Duration as N'#Duration'
Thought I'd share my 2 cents. This fixes the overflow problems but only works with datetime not datetime2. It probably does not work with leap years or when clocks go backwards/forwards. I haven't tested with either.
declare #startTime datetime = getdate()
declare #endTime datetime
select [StartDate] = #startTime,
[EndDate] = #endTime,
[DD:HH:MM:SS.MS] = right( '00' + convert( varchar(20), datediff(hh, 0, #endTime - #startTime ) / 24 ), 2) + ':' +
right( '00' + convert( varchar(20), datediff(hh, 0, #endTime - #startTime ) % 24 ), 2) + ':' +
substring( convert( varchar(20), #endtime - #startTime, 114 ),
charindex( ':', convert( varchar(20), #endTime - #startTime, 114 ) ) + 1,
len( convert( varchar(20), #endTime - #startTime, 114 ) ) )

Calculating timespan with t-sql

Given two date/times:
#start_date = '2009-04-15 10:24:00.000'
#end_date = '2009-04-16 19:43:01.000'
Is it possible to calculate the time elapsed between the two dates in the following format
1d 9h 19m
You can get the difference between the two dates to whatever resolution you want (in your example, minutes):
DATEDIFF(minute, #start_date, #end_date)
From there it's a simple matter of dividing minutes into hours and hours into days and modding the remainder.
I know this thread is older and the original participants are likely no longer watching, but I stumbled upon it, and had already written some code fairly recently to do something very close to what jdiaz is requesting. The result is rendered as a string in D:H:M:S format.
Step one would be to get the time span in seconds:
DECLARE #ElapsedS INT
SET #ElapsedS = DATEDIFF(second, #start_date, #end_date)
Now create the following scalar function:
CREATE FUNCTION [dbo].[udfTimeSpanFromSeconds]
(
#Seconds int
)
RETURNS varchar(15)
AS
BEGIN
DECLARE
--Variable to hold our result
#DHMS varchar(15)
--Integers for doing the math
, #Days int --Integer days
, #Hours int --Integer hours
, #Minutes int --Integer minutes
--Strings for providing the display
, #sDays varchar(5) --String days
, #sHours varchar(2) --String hours
, #sMinutes varchar(2) --String minutes
, #sSeconds varchar(2) --String seconds
--Get the values using modulos where appropriate
SET #Hours = #Seconds/3600
SET #Minutes = (#Seconds % 3600) /60
SET #Seconds = (#Seconds % 3600) % 60
--If we have 24 or more hours, split the #Hours value into days and hours
IF #Hours > 23
BEGIN
SET #Days = #Hours/24
SET #Hours = (#Hours % 24)
END
ELSE
BEGIN
SET #Days = 0
END
--Now render the whole thing as string values for display
SET #sDays = convert(varchar, #Days)
SET #sHours = RIGHT('0' + convert(varchar, #Hours), 2)
SET #sMinutes = RIGHT('0' + convert(varchar, #Minutes), 2)
SET #sSeconds = RIGHT('0' + convert(varchar, #Seconds), 2)
--Concatenate, concatenate, concatenate
SET #DHMS = #sDays + ':' + #sHours + ':' + #sMinutes + ':' + #sSeconds
RETURN #DHMS
END
Now feed your timespan into the newly created function:
SELECT TimeSpan = dbo.udfTimeSpanFromSeconds(#ElapsedS)
Should produce '1:09:19:01'
CONVERT(
varchar(8),
(
CAST(#end_date AS DATETIME)
-
CAST(#start_date AS DATETIME)
)
,108
)
This'll give it to you as HH:MM:SS
Cheers
DATEDIFF can return unintuitive values. For example, the two dates below differ by one second yet DATEDIFF with the parameters below and interpreted as others have interpreted it above returns 1 year:
SELECT DATEDIFF(year, '2005-12-31 23:59:59', '2006-01-01 00:00:00')
Look at the MSDN documentation for DATEDIFF to understand how it works.
datediff(datepart, date1, date2);
Rex's answer is more complete.
Here's how you format the datediff (50d 8h 35m) in a query:
Declare #Date1 as Datetime, #Date2 as Datetime
Set #Date1 = '2005-01-01 08:00:00'
Set #Date2 = '2005-02-20 16:35:30'
Select
CAST(DATEDIFF(Minute,#Date1, #Date2)/60/24 as Varchar(50)) ++ 'd ' ++
CAST((DATEDIFF(Minute,#Date1, #Date2)/60)-((DATEDIFF(Minute,#Date1, #Date2)/60/24)*24) as Varchar(50)) ++ 'h ' ++
CAST((DATEDIFF(Minute,#Date1, #Date2)) - (DATEDIFF(HOUR,#Date1, #Date2)*60) as Varchar(50)) ++ 'm' as FormattedDateDiff
DECLARE #FirstDate DATETIME, #SecondDate DATETIME, #result VARCHAR(MAX)
SELECT #FirstDate = '2017-03-01 09:54:00.637', #SecondDate = GETDATE()
DECLARE #Day INT,#Month INT,#Hour INT, #Minute INT,#TotalSeconds INT,#Year INT
SELECT #TotalSeconds = ABS(DATEDIFF(SECOND,#FirstDate,#SecondDate))
-- Standard values in seconds
DECLARE #YearSeconds INT, #MonthSeconds INT, #DaySeconds INT, #HourSeconds INT, #MinuteSeconds INT
SELECT #MinuteSeconds = 60
SELECT #HourSeconds = 60 * #MinuteSeconds
SELECT #DaySeconds = 24 * #HourSeconds
SELECT #MonthSeconds = 30 * #DaySeconds
SELECT #YearSeconds = 12 * #MonthSeconds
--SELECT #MinuteSeconds AS [Minutes], #HourSeconds AS [Hours], #DaySeconds AS [Day],#MonthSeconds AS [Month],#YearSeconds AS [Year]
IF #TotalSeconds < #MinuteSeconds
BEGIN
SELECT #result = CAST(#TotalSeconds AS NVARCHAR(20)) + ' seconds ago'
END
ELSE IF #TotalSeconds < #HourSeconds
BEGIN
SELECT #result = CAST(ABS(DATEDIFF(MINUTE,#FirstDate,#SecondDate)) AS NVARCHAR(20)) + ' minutes ago'
END
ELSE IF #TotalSeconds < #DaySeconds
BEGIN
SELECT #result = CAST(ABS(DATEDIFF(HOUR,#FirstDate,#SecondDate)) AS NVARCHAR(20)) + ' hours ago'
END
ELSE IF #TotalSeconds < #MonthSeconds
BEGIN
SELECT #result = CAST(ABS(DATEDIFF(DAY,#FirstDate,#SecondDate)) AS NVARCHAR(20)) + ' days ago'
END
ELSE IF #TotalSeconds < #YearSeconds
BEGIN
SELECT #result = CAST(ABS(DATEDIFF(MONTH,#FirstDate,#SecondDate)) AS NVARCHAR(20)) + ' months ago'
END
ELSE IF #TotalSeconds > #YearSeconds
BEGIN
SELECT #result = CAST(ABS(DATEDIFF(YEAR,#FirstDate,#SecondDate)) AS NVARCHAR(20)) + ' year ago'
END
SELECT #result