SQL Server : decimal difference in time - sql

I am trying to convert decimal hours difference in time however always round up causing problem.
declare #Accrued decimal(9,4) = 24.60
declare #Used decimal(9,4) = 23.5734
declare #Remaining decimal(9,2) = #Accrued - #Used
declare #RoundAccrued decimal(9,2) = ceiling(#Accrued * 100) / 100
declare #RoundUsed decimal(9,2) = ceiling(#Used * 100) / 100
declare #RoundRemaining decimal(9,2) = ceiling(#Remaining * 100) / 100
declare #hrAcc int = #Accrued
select left(cast (#hrAcc as varchar), 4) + '.' + right('0' + cast((floor((#RoundAccrued * 60) % 60)) as varchar), 2) Accrued
declare #hrUsed int = #Used
select left(cast (#hrUsed as varchar), 4) + '.' + right('0' + cast((floor((#RoundUsed * 60) % 60)) as varchar), 2) Used
declare #hrRem int = #Remaining
select left(cast (#hrRem as varchar), 4) + '.' + right('0' + cast((floor((#RoundRemaining * 60) % 60)) as varchar), 2) Remaining
Results
Accrued: 24.36
Used: 23.34
Remaining: 1.01 (instead of 1.02)
It works perfectly on Accrued and Used, but whatever I did, it is still either one minute short or more than the different time.

It's all happening because of using rounding, ceiling and floor.
You can use something like below:
select convert(varchar(10), #hrAcc) + '.'+ right('0' + cast(cast((#Accrued-#hrAcc)*60 as int) as varchar),2)
select convert(varchar(10), #hrUsed) + '.'+ right('0' + cast(cast((#Used-#hrUsed)*60 as int) as varchar),2)
select cast((abs(cast(#hrAcc*60+cast((#Accrued-#hrAcc)*60 as int) as int) - cast(#hrUsed*60+cast((#Used-#hrUsed)*60 as int) as int)))/60 as varchar)
+'.'
+ cast((abs(cast(#hrAcc*60+cast((#Accrued-#hrAcc)*60 as int) as int) - cast(#hrUsed*60+cast((#Used-#hrUsed)*60 as int) as int)))%60 as varchar)
---Test (Using rounded values instead of raw)
declare #Accrued decimal(9,4) = 0.5333--106.325--106.325--51.2568--49.9217--106.325--49.9217--24.60
declare #Used decimal(9,4) = 0.0333--87.885--87.885--49.9217--51.2568--87.88--51.2568--23.5734
declare #Remaining decimal(9,4) = #Accrued - #Used
declare #hrAcc int = #Accrued
declare #hrUsed int = #Used
declare #hrRem int = #Remaining
declare #RoundAccrued decimal(9,4) = ceiling(#Accrued * 100) / 100
declare #RoundUsed decimal(9,4) = ceiling(#Used * 100) / 100
declare #RoundRemaining decimal(9,4) = ceiling(#Remaining * 100) / 100
select convert(varchar(10), #hrAcc) + '.'+ right('0' + cast(cast((#RoundAccrued-#hrAcc)*60 as int) as varchar),2) Accrued
select convert(varchar(10), #hrUsed) + '.'+ right('0' + cast(cast((#RoundUsed-#hrUsed)*60 as int) as varchar),2) Used
select cast((abs(cast(#hrAcc*60+cast((#RoundAccrued-#hrAcc)*60 as int) as int) - cast(#hrUsed*60+cast((#RoundUsed-#hrUsed)*60 as int) as int)))/60 as varchar)
+'.'
+ cast((abs(cast(#hrAcc*60+cast((#RoundAccrued-#hrAcc)*60 as int) as int) - cast(#hrUsed*60+cast((#RoundUsed-#hrUsed)*60 as int) as int)))%60 as varchar) Remaining

It should be sufficient to use seconds. So try this:
select cast(dateadd(second, #Remaining*60*60, 0) as time)
This gives the result as a time data type. Note that this type is limited to 24 hours.

Related

Convert SUM of integer to HH:MM format

I have time stored in minutes in integer datatype which has to be displayed in HH:MM format. For example: if total minutes is 80 then it should convert to 01:20.
select SUM(OTTime) from dbo.TableOT where ....
I have tried some queries but didn't get the exact output.
Updated Query:
SELECT SUM(t.OTTime),d.combovalue
FROM dbo.employee e
join dbo.OT t
on e.id = t.employeeid
JOIN dbo.combovalues d
ON e.department = d.id
GROUP By d.combovalue
Try this Single Query:
DECLARE #Duration int
SET #Duration= 4000
SELECT CAST( CAST((#Duration) AS int) / 60 AS varchar) + ':' + right('0' + CAST(CAST((#Duration) AS int) % 60 AS varchar(2)),2)
For Updated Query:
SELECT d.combovalue,CAST(CAST((SUM(t.OTTime)) AS int) / 60 AS varchar) + ':'
+ right('0' + CAST(CAST((SUM(t.OTTime)) AS int) % 60 AS varchar(2)),2)
FROM dbo.employee e join dbo.OT t on e.id = t.employeeid
JOIN dbo.combovalues d ON e.department = d.id
GROUP By d.combovalue
Instead of #minutes variable you can use SUM(OTTime) and a from clause in this below Query
DECLARE #minutes INT=80
SELECT CASE WHEN #minutes >= 60 THEN (SELECT CAST((#minutes / 60) AS VARCHAR(2)) + ':' + CASE WHEN (#minutes % 60) > 0 THEN CAST((#minutes % 60) AS VARCHAR(2))
ELSE '' END)
ELSE CAST((#minutes % 60) AS VARCHAR(2)) END
For Eg:
SELECT CASE WHEN SUM(OTTime) >= 60 THEN (SELECT CAST((SUM(OTTime) / 60) AS VARCHAR(2)) + ':' + CASE WHEN (SUM(OTTime) % 60) > 0 THEN CAST((SUM(OTTime) % 60) AS VARCHAR(2))
ELSE '' END)
ELSE CAST((SUM(OTTime) % 60) AS VARCHAR(2)) END
from dbo.TableOT where ....
Create a SCALAR Function and pass the
select dbo.Minutes_to_HrsMts (SUM(OTTime)) from dbo.TableOT where ....
Function:
CREATE Function dbo.Minutes_to_HrsMts (#minutes INT)
RETURNS nvarchar(30)
AS
BEGIN
declare #hours nvarchar(20)
SET #hours =
CASE WHEN #minutes >= 60 THEN
(SELECT CAST((#minutes / 60) AS VARCHAR(2)) + ':' +
CASE WHEN (#minutes % 60) > 0 THEN
CAST((#minutes % 60) AS VARCHAR(2))
ELSE
''
END)
ELSE
CAST((#minutes % 60) AS VARCHAR(2))
END
return #hours
END
First, I recommend using decimal hours. Much simpler:
SELECT d.combovalue, SUM(t.OTTime) / 60.0
FROM dbo.employee e JOIN
dbo.OT t
ON e.id = t.employeeid JOIN
dbo.combovalues d
ON e.department = d.id
GROUP By d.combovalue;
But that is not your question. If you knew that there are never more than 24 hours, you could use the TIME data type:
CONVERT(VARCHAR(5), DATEADD(minute, SUM(t.OTTime), 0), 8)
If that is too dangerous, then you need to convert to a string:
CONCAT(FORMAT(SUM(t.OTTime) / 60, '00'), ':', FORMAT(SUM(t.OTTime) % 60, '00'))

Multiply TIME in SQL Server

I have this query below, basically I'm trying to subtract 2 dates and get the hours.
However, I need the subtracted time to be multiplied by the number of cleaners
SELECT
CONVERT(TIME, ClientBooking.TimeEnd - ClientBooking.TimeStart) AS HoursWorked2,
ClientBooking.NumberOfCleaners AS NumberOfCleaners,
ClientBooking.TimeStart,
ClientBooking.TimeEnd,
ClientBooking.ClientID,
((((ClientInfo.FirstName + N' ') +
ClientInfo.LastName) + N' ') +
ClientInfo.Company) AS ClientName,
((((ClientInfo.Address + N' - ') +
ClientInfo.City) + N' - ') +
ClientInfo.ZipCode) AS Address,
((ClientInfo.PhoneNumber + N' ') +
ClientInfo.EmailAddress) AS Contact,
(ClientBooking.HourlyRate / 60) AS MinRate,
(DATEDIFF(MINUTE,ClientBooking.TimeStart,ClientBooking.TimeEnd) * ClientBooking.NumberOfCleaners) AS Quantity,
ClientBooking.HourlyRate,
DATEDIFF(HOUR, ClientBooking.TimeStart, ClientBooking.TimeEnd) AS HoursWorked
FROM
(dbo.ClientBooking ClientBooking
INNER JOIN
dbo.ClientInfo ClientInfo ON (ClientInfo.ClientID = ClientBooking.ClientID))
Basically, I need to multiply the result of this:
CONVERT(TIME,"ClientBooking"."TimeEnd" - "ClientBooking"."TimeStart" )
How About using this:
Select
convert(time,DATEADD(MINUTE, ( convert(float,(DATEDIFF(minute, ClientBooking.TimeStart, ClientBooking.TimeEnd) * ClientBooking.NumberOfCleaners))/60), ''))
FROM
(dbo.ClientBooking ClientBooking
INNER JOIN
dbo.ClientInfo ClientInfo ON (ClientInfo.ClientID = ClientBooking.ClientID))
Sorry if i have missed a parenthesis !!
You can use DATEDIFF() function..
Something like:
DATEDIFF(hour, ClientBooking.TimeStart, ClientBooking.TimeEnd) * ClientBooking.NumberOfCleaners
as your desired column!
If I understand you correctly this could help you:
declare #start datetime = '2018-11-02 07:00:00'
declare #end datetime = '2018-11-02 08:03:00'
declare #diff int
Select #diff = DATEDIFF(minute,#start,#end)
Select case
when #diff < 60 then concat('00:', right('0' + convert(varchar,#diff), 2))
when #diff >= 60 and #diff < 120 then '01:' + right('0' + convert(varchar,#diff - 60), 2)
when #diff >= 120 and #diff < 180 then '02:' + right('0' + convert(varchar,#diff - 120), 2)
when #diff >= 180 and #diff < 240 then '03:' + right('0' + convert(varchar,#diff - 180), 2)
end
Of course you would need to add the following hours as well.
I've splitted everything up, so it is easier to understand. But you should be able to write it in one line and without variables as well
Hope this helps.

How to format time from dd:hh:mm:ss to only hh:mm:ss in SQL server?

I found SQL function which get second as input parameter and return seconds in dd:hh:mm:ss format e-g for 93600 seconds it returns 1:02:00:00
it means 1 day 2 hours 0 minutes and 0 seconds.
Function that i used is :
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
and select command that will retrieve output is
select dbo.udfTimeSpanFromSeconds('93600' )
it shows me result as:
Now i need this output in hh:mm:ss format e-g for current example 26:00:00 it means 26 hours 0 minutes and 0 seconds.
I am using SQL server 2008. Thanks in advance.
You can do this with math
DECLARE #sec INT = 93600
SELECT
CONVERT(VARCHAR(10), (#sec / 3600)) + ':' +
RIGHT('0' + CONVERT(VARCHAR(2), ((#sec % 3600) / 60)), 2) + ':' +
RIGHT('0' + CONVERT(VARCHAR(2), (#sec % 60)), 2)
Written as a function:
CREATE FUNCTION udfTimeSpanFromSeconds(
#sec INT
)
RETURNS VARCHAR(15)
AS
BEGIN
RETURN
CONVERT(VARCHAR(10), (#sec / 3600)) + ':' +
RIGHT('0' + CONVERT(VARCHAR(2), ((#sec % 3600) / 60)), 2) + ':' +
RIGHT('0' + CONVERT(VARCHAR(2), (#sec % 60)), 2)
END
Sample call:
SELECT dbo.udfTimeSpanFromSeconds(360000)
RESULT:
100:00:00
If you want you function to return hh:mm:ss then it needs to be as below. This does however limit the total time to be less than 100 hours. You can fix this by increasing the length of the Hours String by changing the right clause and increasing the length of the string returned, as I have now done to illustrate.
(Normally once you have summed your time you usually divide the total by 3600.00 to produce a decimal value for use in further calculations, for example if you are paying by the hour)
FUNCTION [dbo].[udfTimeSpanFromSeconds]
(
#Seconds int
)
RETURNS varchar(10)
AS
BEGIN
DECLARE
--Variable to hold our result
#HMS varchar(15)
--Integers for doing the math
, #Hours int --Integer hours
, #Minutes int --Integer minutes
--Strings for providing the display
, #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
--Now render the whole thing as string values for display
SET #sHours = RIGHT('0' + convert(varchar(5), #Hours), 3)
SET #sMinutes = RIGHT('0' + convert(varchar(3), #Minutes), 2)
SET #sSeconds = RIGHT('0' + convert(varchar(3), #Seconds), 2)
--Concatenate, concatenate, concatenate
SET #HMS = #sHours + ':' + #sMinutes + ':' + #sSeconds
RETURN #HMS
END
If you want to return 100 hours then you have to use it:-
FUNCTION [dbo].[udfTimeSpanFromSeconds]
(
#Seconds int
)
RETURNS varchar(8)
AS
BEGIN
DECLARE
--Variable to hold our result
#HMS varchar(15)
--Integers for doing the math
, #Hours int --Integer hours
, #Minutes int --Integer minutes
--Strings for providing the display
, #sHours varchar(3) --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
--Now render the whole thing as string values for display
SET #sHours = RIGHT('0' + convert(varchar, #Hours), 3)
SET #sMinutes = RIGHT('0' + convert(varchar, #Minutes), 2)
SET #sSeconds = RIGHT('0' + convert(varchar, #Seconds), 2)
--Concatenate, concatenate, concatenate
SET #HMS = #sHours + ':' + #sMinutes + ':' + #sSeconds
RETURN #HMS
END

if minute is less than 60 then remove hour part from result

I have a function like this:
ALTER FUNCTION [dbo].[testfunctionstacknew] (#dec NUMERIC(18, 2)) RETURNS Varchar(50)
AS
BEGIN
DECLARE
#hour decimal(18,2),
#Mns decimal(18,2),
#second decimal(18,3)
DECLARE #Average Varchar(50)
select #hour=CONVERT(int,#dec/60/60)
SELECT #Mns = convert(int, (#dec / 60) - (#hour * 60 ));
select #second=#dec % 60;
SELECT #Average =
convert(varchar(9), convert(int, #hour)) + ':' +
-- right('00' + convert(decimal(10,0), convert(decimal(18,2), #hour)), 2) + ':' +
right('00' + convert(decimal(10,0), convert(decimal(18,2), #Mns)), 2) + ':' +
right('00' + CONVERT(decimal(10,0), convert(varchar(10), #second)), 6)
RETURN #Average
END
and i have a stored procedure like this:
select dbo.testfunctionstacknew(
convert(decimal(10,1),
avg(convert(numeric(18,2), datediff(ss, t.dtime, t.PAICdate ))))
) as Avgparkingtime from (select top 10 * from transaction_tbl where locid=6 and dtime >= getdate()-1 order by transactID desc ) t
while executing stored procedure if the average time is less than 60 minutes i am getting result like this:
Avgparkingtime :
0:25:33
if the average time is less than 60 minutes then i dont want to get zero in front of minutes,,(this time only need to show minutes and seconds)..only hour need to show if the minutes is greater than 60 minutes...how i can do this? what changes i have to make in my function ?? any help is very appreciable..
Try changing the part where you build the formatted string like this:
SELECT #Average =
case
when convert(int, #hour) <> 0 then convert(varchar(9), convert(int, #hour)) + ':'
else ''
end +
right('00' + convert(decimal(10,0), convert(decimal(18,2), #Mns)), 2) + ':' +
right('00' + CONVERT(decimal(10,0), convert(varchar(10), #second)), 6)

SQL: float number to hours format

Is there a easy way to format a float number in hours in Ms SQL server 2008?
Examples:
1.5 -> 01:30
9.8 -> 09:48
35.25 -> 35:15
Thanks a lot.
I like this question!
DECLARE #input float = 1.5;
DECLARE #hour int = FLOOR(#input);
DECLARE #minutes int = (SELECT (#input - FLOOR(#input)) * 60);
SELECT RIGHT('00' + CONVERT(varchar(2), #hour), 2) + ':' + RIGHT('00' + CONVERT(varchar(2), #minutes), 2);
SELECT SUBSTRING(CONVERT(NVARCHAR, DATEADD(MINUTE, 1.5*60, ''), 108), 1, 5)
This works by:
starting from the "zero" date
adding 1.5 x 60 minutes (i.e. 1.5 hours)
formatting the result as a time, hh:mm:ss (i.e. format "108")
trimming off the seconds part
It is necessary to use 1.5 x 60 minutes instead of 1.5 hours as the DATEADD function truncates the offset to the nearest integer. If you want high-resolution offsets, you can use SECOND instead, suitable scaled (e.g. hours * 60 * 60).
Sure. Easy, but not exactly...straightforward:
declare #hours float
set #hours = -9.8
select substring('- ',2+convert(int,sign(#hours)),1) -- sign
+ right('00' + convert(varchar, floor(abs(#hours))) , 2 ) -- hours component
+ ':' -- delimiter
+ right('00' + convert(varchar,round( 60*(abs(#hours)-floor(abs(#hours))) , 0 ) ) , 2 ) -- minutes
Another option that will give the correct result. You might need to tweak it to round minutes and to ensure that both fields are 2 digits wide.
declare #hours float
set #hours = -9.8
select convert(varchar, datediff(minute,dateadd(minute,#hours*60,convert(datetime,'')),'') / 60 )
+ ':' + convert(varchar, datediff(minute,dateadd(minute,#hours*60,convert(datetime,'')),'') % 60 )
WITH m AS
SELECT Minutes = CAST(#hours * 60 AS int)
)
SELECT CAST(Minutes / 60 AS varchar) + ':' + RIGHT(100 + Minutes % 60, 2)
FROM m
select dateadd(MINUTE, cast((8.18 % 1) * 60 as int), dateadd(hour, cast(8.18 as int), convert(varchar(10), getdate(), 10)))