Subtract two time values in SQL Server - sql-server

How to subtract two time values in SQL Server 2008. I am using time variables in a stored procedure.
Please help.

You can use DATEDIFF():
SELECT DATEDIFF(Day, startDate, endDate)
FROM table
SELECT DATEDIFF(Second, date, GETDATE())
FROM table

DECLARE #END TIME = '16:00:00.0000000' ,
#START TIME = '01:00:00.0000000'
SELECT convert(TIME,dateadd(ms,DateDiff(ss, #START, #END )*1000,0),114)

following expression works for me
declare #starttime Time, #endtime Time
set #starttime='18:45'
set #endtime='22:45'
select DATEDIFF(HH,#starttime, #endtime)
output: 4

Even You are using offset value or normal dates this code will give you appropriate answers.
BEGIN TRY
DECLARE #OffSetVal1 VARCHAR(39) = '2019-12-02 09:15:29 +14:00'
, #OffSetVal2 VARCHAR(39) = '2019-12-02 09:15:29 +12:30'
, #minutes INT = 0
, #OffSetDiff VARCHAR(19) = ''
, #HourDiff INT = 0
SET #HourDiff = DATEDIFF(HOUR,#OffSetVal1,#OffSetVal2) -- To Check digits of hours.
SET #minutes = DATEDIFF(MINUTE,#OffSetVal1,#OffSetVal2) -- To Convert minutes to hours.
SET #OffSetDiff = #minutes / 60 + (#minutes % 60) / 100.0 -- To Check '+' Or '-' And To Get Value.
SELECT CASE WHEN CAST(#OffSetDiff AS FLOAT) <= 0
THEN (CASE WHEN #HourDiff < 10
THEN FORMAT(CAST(#OffSetDiff AS FLOAT),'0#.00')
ELSE FORMAT(CAST(#OffSetDiff AS FLOAT),'0#.00')
END)
ELSE (CASE WHEN #HourDiff < 10
THEN '+'+FORMAT(CAST(#OffSetDiff AS FLOAT),'0#.00')
ELSE '+'+FORMAT(CAST(#OffSetDiff AS FLOAT),'0#.00')
END)
END
END TRY
BEGIN CATCH
PRINT N'It seems you provided an invalid DateTimeOffSet Parameter. '
END CATCH
ANS :- +01.30 (# The offset differences with hour and minute and if you don't want that '+' simply remove from code & then run the code)

Related

How can you add working hours to date time

I'm looking for a function where i can define working hours eg 9am - 5 pm and then add working hours to a datetime eg datetime 2021-08-05 16:30:00 add 3 working hours so the date time would be 2021-08-06 11:30:00.
I currently do this with working days see code below
This is to calculate SLA on a task eg task comes in at 2021-08-05 16:30:00 SLA is 3 working hours so it needs to be completed by 2021-08-06 11:30:00.
Any help would be appreciated
Thanks
declare
#ReturnDate datetime,
#StartDate datetime,
#CountDays int,
#Country varchar (255)
set #StartDate = '2021-08-05 16:30:00.000'
set #CountDays = 2
set #Country = 'England and Wales'
;with cteWorkingDates as
(
select DAT.Calendar_Date_Date,
ROW_NUMBER () over (
order by DAT.Calendar_Date_Date
) as RowNo
from dwBoyce.archway.Calendar_Date DAT with (nolock)
left join dwBoyce.archway.GovUK_BankHolidays BAH with (nolock) on DAT.Calendar_Date_Date = BAH.GovUK_BankHolidays_Date
and #Country = BAH.GovUK_BankHolidays_Country
where DAT.Calendar_Date_Date > #StartDate
and DAT.Calendar_Date_WorkingDay = 1
and BAH.GovUK_BankHolidays_Date is null
)
select cast(wd.Calendar_Date_Date as datetime) + DATEADD(day, 0 - DATEDIFF(day, 0, #StartDate), #StartDate)
from cteWorkingDates WD
where WD.RowNo = #CountDays
You might want to try the following:
Evaluate, how many minutes of working time are remaining starting from the time the ticket has been created. Then calculate the number of minutes the SLA request may take (in your case 3 * 60 = 180 minutes). When the remaining working time is less than the SLA time in minutes, the answer time will be Ticket time + 1 day + (SLA minutes - remaining working time in minutes).
Following an example:
DECLARE #WorkingHoursStart TIME = '09:00:00.000';
DECLARE #WorkingHoursEnd TIME = '17:00:00.000';
DECLARE #WorkingHoursCount INT = DATEDIFF(HOUR, #WorkingHoursStart, #WorkingHoursEnd);
DECLARE #SLAHours INT = 32;
DECLARE #TicketTimestamp DATETIME = '2022-11-01 12:30:00';
DECLARE #TicketWorkingHoursStart DATETIME = DATETIMEFROMPARTS(YEAR(#TicketTimestamp)
,MONTH(#TicketTimestamp)
,DAY(#TicketTimestamp)
,DATEPART(HOUR, #WorkingHoursStart)
,DATEPART(MINUTE, #WorkingHoursStart)
,0
,0);
DECLARE #TicketWorkingHoursEnd DATETIME = DATETIMEFROMPARTS(YEAR(#TicketTimestamp)
,MONTH(#TicketTimestamp)
,DAY(#TicketTimestamp)
,DATEPART(HOUR, #WorkingHoursEnd)
,DATEPART(MINUTE, #WorkingHoursEnd)
,0
,0);
DECLARE #TicketWorkingTimestamp DATETIME = CASE
WHEN #TicketTimestamp < #TicketWorkingHoursStart
THEN #TicketWorkingHoursStart
WHEN #TicketTimestamp > #TicketWorkingHoursEnd
THEN DATEADD(d, 1, #TicketWorkingHoursStart)
ELSE #TicketTimestamp
END;
DECLARE #TmpTimeStamp DATETIME = DATEADD(d, #SLAHours/#WorkingHoursCount, #TicketWorkingTimestamp);
DECLARE #TmpSLAHours INT = #SLAHours - ((#SLAHours/#WorkingHoursCount)*#WorkingHoursCount);
DECLARE #TmpSLAMinutes INT = #TmpSLAHours * 60;
DECLARE #AnswerTimestamp DATETIME = CASE
WHEN DATEDIFF(MINUTE, CAST(#TmpTimeStamp AS TIME), #WorkingHoursEnd) < #TmpSLAMinutes
THEN DATEADD(MINUTE
,#TmpSLAMinutes - DATEDIFF(MINUTE
,CAST(#TmpTimeStamp AS TIME)
,#WorkingHoursEnd)
,DATEADD(DAY
,CASE WHEN #TmpSLAHours/#WorkingHoursCount = 0 THEN 1 ELSE 0 END
,DATETIMEFROMPARTS(YEAR(#TmpTimeStamp)
,MONTH(#TmpTimeStamp)
,DAY(#TmpTimeStamp)
,DATEPART(HOUR, #WorkingHoursStart)
,DATEPART(MINUTE, #WorkingHoursStart)
,DATEPART(SECOND, #TmpTimeStamp)
,0)
)
)
ELSE DATEADD(MINUTE, #TmpSLAMinutes, #TmpTimeStamp)
END
DECLARE #WeekEndDaySkip INT = ((DATEPART(WEEKDAY, #TicketTimestamp)+DATEDIFF(d, #TicketTimestamp, #AnswerTimestamp))/7)*2
SELECT #TicketTimestamp AS TicketTime
, #AnswerTimestamp AS AnswerTimestamp
, DATEADD(d, #WeekEndDaySkip, #AnswerTimestamp) AS AnswerTimestampWOWeekend
Adding this deals with SLAs that span over weekends
declare
#date1 date = cast(#TicketTimestamp as date)
,#date2 date = cast(#TmpTimeStamp as date)
declare #Count int
Select #Count = count(*)
from archway.Calendar_Date d
where d.Calendar_Date_Date between #date1 and #date2
and Calendar_Date_DayName in ('Saturday','Sunday')
DECLARE #TmpTimeStamp1 DATETIME = case when #count = 2 then DATEADD(d,#count,#TmpTimeStamp)
else #TmpTimeStamp end

SQL server stored procedure timeouts

The following stored procedure takes almost 2 minutes to run which is causing a time out. Both tables do have primary keys and indexes defined. Is there anyway I can improve the process time?
ALTER PROCEDURE [dbo].[dfc_rpt_validation]
-- declare
#region varchar(10) = null,
#source varchar(10) = null
AS BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
select row_number() OVER(ORDER BY l.loan_code) as Id
, l.region AS Region
, l.source AS Source
, l.loan_code_string AS CL_Loan_Code
, l.loan_principal_amt AS CL_Loan_Principal_Amt
, l.loan_amt_due AS CL_Loan_Amt_Due
, l.date_originated AS CL_Origination_Date
, l.StoreNumber AS CL_Store_Number
, v.new_loan_code_string AS FA_Loan_Code
, v.loan_principal_amt AS FA_Loan_Principal_Amt
, v.loan_amt_due AS FA_Loan_Amt_Due
, v.loan_origination_date AS FA_Origination_Date
, (select count(*) from [dbo].[dfc_LoanValidatedRecords] s WHERE s.loan_code_string = l.loan_code_string) AS FA_Times_Loan_Document_Processed
, (CASE WHEN l.rejected_date IS NULL AND l.validated_date IS NULL THEN ''
WHEN v.pdf_legible = 0 THEN 'operational reject' -- operational error
WHEN v.loan_code_valid = 1
AND v.loan_principal_amt_valid = 1
AND v.loan_origination_date_valid = 1
AND v.loan_amt_due_valid = 1
--OR v.pdf_legible = 0 -- operational error
THEN 'validated' ELSE 'rejected' END
) AS [FA_Verification_Status]
-- 100 delivery method failure
-- 200 pdf reject codes
-- 400 borrower info reject codes
-- 600 loan reject codes
, LTRIM(RTRIM (
--(CASE WHEN l.rejected_date IS NULL AND l.validated_date IS NULL THEN ''
--ELSE
(CASE WHEN v.pdf_legible = 0 THEN ' 200'
ELSE
(CASE WHEN v.loan_code_valid = 0 THEN ' 600' ELSE '' END)
+ (CASE WHEN v.loan_principal_amt_valid = 0 THEN ' 610' ELSE '' END)
+ (CASE WHEN v.loan_origination_date_valid = 0 THEN ' 620' ELSE '' END) -- LoanDate
+ (CASE WHEN v.loan_amt_due_valid = 0 THEN ' 625' ELSE '' END)
END) -- operational error
--END)
)) AS FA_Reason
, l.load_file AS load_file
from dfc_LoanRecords AS l
JOIN dfc_LoanValidatedRecords AS v ON v.loan_code_string = l.loan_code_string
WHERE CONVERT (DATE, l.load_date) >= convert(date, dateadd(hh,-8,getdate())) -- handle UTC conversion
AND l.region = #region AND l.source = #source
ORDER BY FA_Verification_Status, FA_Reason
END
Simplest change I can see:
There's no reason I can see for this: CONVERT (DATE, l.load_date).
Just use l.load_date >= convert(date, dateadd(hh,-8,getdate()))
This should enable SQL query optimiser to use any indexes that might exist on load_date. At the moment it can't do so efficiently as you're using a function on the column.
Wouldn't be better if instead of converting the Convert(date, dateadd(hh,-8,getdate()))
in the query itself to avoid that function for every record
DECLARE #Date DATE = CAST(DATEADD(HH,-8,GETDATE()) AS DATE)
WHERE CONVERT (DATE, l.load_date) >= #Date -- handle UTC conversion
AND l.region = #region AND l.source = #source
With this you are going to get a better execution plan instead of converting the value for every single row.
Also if you can apply the ORDER BY at the application layer... please read 7 Things Developers Should Know About SQL Server item 2 please do so, there's no need to use it on the database side unless we have to, it will reduce extra-cost on tempdb.

How to convert number of minutes to hh:mm format in TSQL?

I have a select query that has DURATION column to calculate number of Minutes . I want to convert those minutes to hh:mm format.
Duration has values like 60, 120,150
For example:
60 becomes 01:00 hours
120 becomes 02:00 hours
150 becomes 02:30 hours
Also, this is how I retrieve DURATION (Minutes)
DATEDIFF(minute, FirstDate,LastDate) as 'Duration (Minutes)'
You can convert the duration to a date and then format it:
DECLARE
#FirstDate datetime,
#LastDate datetime
SELECT
#FirstDate = '2000-01-01 09:00:00',
#LastDate = '2000-01-01 11:30:00'
SELECT CONVERT(varchar(12),
DATEADD(minute, DATEDIFF(minute, #FirstDate, #LastDate), 0), 114)
/* Results: 02:30:00:000 */
For less precision, modify the size of the varchar:
SELECT CONVERT(varchar(5),
DATEADD(minute, DATEDIFF(minute, #FirstDate, #LastDate), 0), 114)
/* Results: 02:30 */
This function is to convert duration in minutes to readable hours and minutes format. i.e 2h30m. It eliminates the hours if the duration is less than one hour, and shows only the hours if the duration in hours with no extra minutes.
CREATE FUNCTION [dbo].[MinutesToDuration]
(
#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)) + 'h' +
CASE WHEN (#minutes % 60) > 0 THEN
CAST((#minutes % 60) AS VARCHAR(2)) + 'm'
ELSE
''
END)
ELSE
CAST((#minutes % 60) AS VARCHAR(2)) + 'm'
END
return #hours
END
To use this function :
SELECT dbo.MinutesToDuration(23)
Results: 23m
SELECT dbo.MinutesToDuration(120)
Results: 2h
SELECT dbo.MinutesToDuration(147)
Results: 2h27m
Hope this helps!
I'm not sure these are the best options but they'll definitely get the job done:
declare #durations table
(
Duration int
)
Insert into #durations(Duration)
values(60),(80),(90),(150),(180),(1000)
--Option 1 - Manually concatenate the values together
select right('0' + convert(varchar,Duration / 60),2) + ':' + right('0' + convert(varchar,Duration % 60),2)
from #Durations
--Option 2 - Make use of the time variable available since SQL Server 2008
select left(convert(time,DATEADD(minute,Duration,0)),5)
from #durations
GO
DECLARE #Duration int
SET #Duration= 12540 /* for example big hour amount in minutes -> 209h */
SELECT CAST( CAST((#Duration) AS int) / 60 AS varchar) + ':' + right('0' + CAST(CAST((#Duration) AS int) % 60 AS varchar(2)),2)
/* you will get hours and minutes divided by : */
For those who need convert minutes to time with more than 24h format:
DECLARE #minutes int = 7830
SELECT CAST(#minutes / 60 AS VARCHAR(8)) + ':' + FORMAT(#minutes % 60, 'D2') AS [Time]
Result:
130:30
This seems to work for me:
SELECT FORMAT(#mins / 60 * 100 + #mins % 60, '#:0#')
Thanks to A Ghazal, just what I needed. Here's a slightly cleaned up version of his(her) answer:
create FUNCTION [dbo].[fnMinutesToDuration]
(
#minutes int
)
RETURNS nvarchar(30)
-- Based on http://stackoverflow.com/questions/17733616/how-to-convert-number-of-minutes-to-hhmm-format-in-tsql
AS
BEGIN
return rtrim(isnull(cast(nullif((#minutes / 60)
, 0
) as varchar
) + 'h '
,''
)
+ isnull(CAST(nullif((#minutes % 60)
,0
) AS VARCHAR(2)
) + 'm'
,''
)
)
end
select convert(varchar(5),dateadd(mi,DATEDIFF(minute, FirstDate,LastDate),'00:00'),114)
In case someone is interested in getting results as
60 becomes 01:00 hours, 120 becomes 02:00 hours, 150 becomes 02:30 hours, this function might help:
create FUNCTION [dbo].[MinutesToHHMM]
(
#minutes int
)
RETURNS varchar(30)
AS
BEGIN
declare #h int
set #h= #minutes / 60
declare #mins varchar(2)
set #mins= iif(#minutes%60<10,concat('0',cast((#minutes % 60) as varchar(2))),cast((#minutes % 60) as varchar(2)))
return iif(#h <10, concat('0', cast(#h as varchar(5)),':',#mins)
,concat(cast(#h as varchar(5)),':',#mins))
end
I would do the following (copy-paste the whole stuff below into immediate window / query window and execute)
DECLARE #foo int
DECLARE #unclefoo smalldatetime
SET #foo = DATEDIFF(minute, CAST('2013.01.01 00:00:00' AS datetime),CAST('2013.01.01 00:03:59' AS datetime)) -- AS 'Duration (Minutes)'
SET #unclefoo = DATEADD(minute, #foo, '2000.01.01')
SELECT CAST(#unclefoo AS time)
#foo stores the value you generate in your question. The "trick" comes by then:
we create a smalldatetime variable (in my case it's yyyy.mm.dd format) and increment it with your int value, then display (or store if you want) the time part only.
declare function dbo.minutes2hours (
#minutes int
)
RETURNS varchar(10)
as
begin
return format(dateadd(minute,#minutes,'00:00:00'), N'HH\:mm','FR-fr')
end
How to get the First and Last Record time different in sql server....
....
Select EmployeeId,EmployeeName,AttendenceDate,MIN(Intime) as Intime ,MAX(OutTime) as OutTime,
DATEDIFF(MINUTE, MIN(Intime), MAX(OutTime)) as TotalWorkingHours
FROM ViewAttendenceReport WHERE AttendenceDate >='1/20/2020 12:00:00 AM' AND AttendenceDate <='1/20/2020 23:59:59 PM'
GROUP BY EmployeeId,EmployeeName,AttendenceDate;
If you want a notation of XX days YY hours and ZZ min, just try:
SELECT
CAST(f.TimeAmount / 1440 AS VARCHAR(8)) + 'd ' +
CAST((f.TimeAmount % 1440) / 60 AS VARCHAR(8)) + 'h ' +
FORMAT(f.TimeAmount % 60, 'D2') + 'min' AS [TIME_TEXT]
FROM
MyTable f

SQL Server - how to dynamically determine financial year?

Every year I have to update my company's financial reports to include the new financial year (as the year isn't coterminus with the calendar year), so I do.....
Case
when ST_date >= '1996.11.01 00:00:00' and st_date < '1997.11.01 00:00:00'
then '96-97'
[etc]
end as year,
Every year I have to remember which reports I need to amend - most years I forget one!
...Is there a simple dynamic way to determine this?
You could definitely write a simple stored function in SQL Server to determine the financial year based on the date:
CREATE FUNCTION dbo.GetFinancialYear (#input DATETIME)
RETURNS VARCHAR(20)
AS BEGIN
DECLARE #FinYear VARCHAR(20)
SET #FinYear =
CASE
WHEN #INPUT >= '19961101' AND #input < '19971101' THEN '96-97'
WHEN #INPUT >= '19971101' AND #input < '19981101' THEN '97-98'
ELSE '(other)'
END
RETURN #FinYear
END
and then just use that in all your queries.
SELECT
somedate, dbo.GetFinancialYear(somedate)
......
If you need to add a new financial year - just update the one function, and you're done !
Update: if you want to make this totally dynamic, and you can rely on the fact that the financial year always starts on Nov 1 - then use this approach instead:
CREATE FUNCTION dbo.GetFinancialYear (#input DATETIME)
RETURNS VARCHAR(20)
AS BEGIN
DECLARE #FinYear VARCHAR(20)
DECLARE #YearOfDate INT
IF (MONTH(#input) >= 11)
SET #YearOfDate = YEAR(#input)
ELSE
SET #YearOfDate = YEAR(#input) - 1
SET #FinYear = RIGHT(CAST(#YearOfDate AS CHAR(4)), 2) + '-' + RIGHT(CAST((#YearOfDate + 1) AS CHAR(4)), 2)
RETURN #FinYear
END
This will return:
05/06 for a date such as 2005-11-25
04/05 for a date such as 2005-07-25
Have a look at this example:
declare #ST_Date datetime = '20120506'
SELECT
convert(char(2),DateAdd(m,-10,#ST_DATE),2)+'-'+
convert(char(2),DateAdd(m,+ 2,#ST_DATE),2) as year
As a column expression:
convert(char(2),DateAdd(m,-10,ST_DATE),2)+'-'+
convert(char(2),DateAdd(m,+ 2,ST_DATE),2) as year
Pretty trivial!
The way I handle these problems (financial year, pay period etc) is to recognize the fact that financial years are the same as any year, except they start X months later. The straightforward solution is therefore to shift the FY by the number of months back to the calendar year, from which to do any "annual" comparisons or derivation of "year" (or "month").
Declare #FinancialMonth Varchar(100)=NULL,#Month smallint,#Date DateTime='04/06/2013'
BEGIN TRY
SELECT #FinancialMonth='01-'+IsNULL(#FinancialMonth,'April')+'-'+Cast(year(getdate()) as varchar)
SELECT #Month=(Month(Cast(#FinancialMonth as datetime))-1) * -1
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,'Invalid Financial Month' ErrorMessage
END CATCH
SELECT Month((CONVERT([varchar](10),dateadd(month,(#Month),#Date),(101)))) FinancialMonth,
Year((CONVERT([varchar](10),dateadd(month,(#Month),#Date),(101)))) FinancialYear
,DatePart(qq,(CONVERT([varchar](10),dateadd(month,(#Month),#Date),(101)))) FinancialQuarter
This one works for me and sets it as the actual FY end date.
SET #enddatefy = convert(DATE, str(datepart(yyyy,DateAdd(m,-6,#enddate))+1)+'0630',112)
SET #enddatefyid = str(datepart(yyyy,DateAdd(m,-6,#enddate))+1)+'0630'
datename(YEAR, DATEADD(M,-3,Date)) +'-'+ cast((datepart(YEAR, DATEADD(M,-3,Date)) + 1) %100 as varchar(2))
Calculate on Column 'Date'
Financial year ranges from 1st April to 31st March
Create FUNCTION dbo.GetFinancialYear (#input DATETIME)
RETURNS VARCHAR(20)
AS BEGIN
DECLARE #FinYear VARCHAR(20)
IF (MONTH(#input) > 3)
SET #FinYear = RIGHT(CAST(Year(#input) AS CHAR(4)), 4) + '-' + RIGHT(CAST((Year(#input) + 1) AS CHAR(4)), 2)
ELSE
SET #FinYear = RIGHT(CAST((Year(#input) - 1) AS CHAR(4)), 4) + '-' + RIGHT(CAST(Year(#input) AS CHAR(4)), 2)
RETURN #FinYear
END
Declare #date1 datetime = '2017-07-01'
Select Case
When Month(#date1)>=7 Then 'FY'+Convert(NVARCHAR(10),(Right(year(getdate()),2)+1))
Else 'FY'+Convert(NVARCHAR(10),(Right(year(getdate()),2)))
End
This works for me, where the financial year starts in July.
CASE WHEN DatePart(mm, [YourDate]) >= 7
THEN convert(varchar(10), YEAR([YourDate])) +' / '+ Convert(varchar(10), YEAR([YourDate]) + 1 )
ELSE Convert(varchar(10), YEAR([YourDate]) - 1) +' / '+ Convert(varchar(10), YEAR([YourDate]) )
END AS [Financial Year],

How to write a calculation query in SQL?

Using SQL Server 2000
My Query.
SELECT
(Format(IIf(CLng(OutTime) > 180000, CDate('18:00:00'), CDate(Format(OutTime, '00:00:00'))) - IIf(CLng(InTime) < 90000, CDate('09:00:00'), CDate(Format(InTime, '00:00:00'))), 'hh:nn:ss')) As WorkTime,
(Format(IIf(CLng(InTime) < 90000, CDate('09:00:00') - CDate(Format(InTime, '00:00:00')), 0) + IIf(CLng(OutTime) > 180000, CDate(Format(OutTime, '00:00:00')) - CDate('18:00:00'), 0), 'hh:nn:ss')) As OverTime
FROM table
Above query is Access Query, I want to write a same query in sql.
Condition.
I want to Calculate the time after 090000(HH:MM:SS) before 180000 comes in worktime, before
090000 after 180000 comes in overtime.
Intime, Outime data type is varchar in the database
Am new to SQL Server 2000
How to write a SQL query from the above same?
Here is literally translated query to TSQL 2000.
Although, it could be rewritten for better performance:
Select Convert(char(8),
case when DateAdd(Day,-DateDiff(Day, 0, OutTime), OutTime)>'18:00:00'
Then Cast('18:00:00' as datetime)
Else DateAdd(Day,-DateDiff(Day, 0, OutTime), OutTime)
End
-
Case when DateAdd(Day,-DateDiff(Day, 0, InTime), InTime) <'09:00:00'
Then Cast('09:00:00' as datetime)
Else DateAdd(Day,-DateDiff(Day, 0, InTime), InTime)
End,
8
) as WorkTime,
Convert(char(8),
Case when DateAdd(Day,-DateDiff(Day, 0, InTime), InTime) <'09:00:00'
Then Cast('09:00:00' as datetime) -
DateAdd(Day,-DateDiff(Day, 0, InTime), InTime)
Else Cast('00:00:00' as datetime)
End
+
case when DateAdd(Day,-DateDiff(Day, 0, OutTime), OutTime)>'18:00:00'
Then DateAdd(Day,-DateDiff(Day, 0, OutTime), OutTime) -
Cast('18:00:00' as datetime)
Else Cast('00:00:00' as datetime)
End,
8
) as OverTime
From Table
Added later:
If InTime and OutTime have time part only (Date part is Jan 1 1900) you can use directly InTime and OutTime. Otherwise you have to extract time part from datetime column, as:
DateAdd(Day,-DateDiff(Day, 0, OutTime), OutTime)
(this is the fastest way to get time part only)
Instead of:
DateAdd(Day,-DateDiff(Day, 0, OutTime), OutTime)>'18:00:00'
You can use
Datepart(hh,OutTime)>17
P.S. as your time is stored as string yoiu don't need to get time part only. You can cast them to datetime, or you can write also
cast(left(inTime,2) as int) < 9
As a starter take a look at this website which shows you how to convert Access SQL statements into T-SQL as used by SQL server.
http://weblogs.sqlteam.com/jeffs/archive/2007/03/30/Quick-Access-JET-SQL-to-T-SQL-Cheatsheet.aspx
I don't think there's a very easy and simple way to do this - most notably because of storing time values in VARCHAR - this is really making this tricky.....
Anyway, I used an approach with two functions - one dbo.GetSeconds that converts a string representation of a time value ('103500' -> 10:35:00 hours) to a number of seconds, and then a second one dbo.GetOvertime that detects if there is any overtime.
CREATE FUNCTION dbo.GetSeconds(#input varchar(20))
RETURNS int
AS BEGIN
DECLARE #Hour INT
DECLARE #Minute INT
DECLARE #Second INT
DECLARE #TotalSeconds INT
SET #Hour = CAST(SUBSTRING(#input, 0, LEN(#input)-3) AS INT)
SET #Minute = CAST(LEFT(RIGHT(#input, 4), 2) AS INT)
SET #Second = CAST(RIGHT(#input, 2) AS INT)
SET #TotalSeconds = #Hour * 3600 + #Minute * 60 + #Second
RETURN #TotalSeconds
END
CREATE FUNCTION dbo.GetOvertime(#fromSeconds INT, #toSeconds INT)
RETURNS int
AS BEGIN
DECLARE #Overtime INT
SET #Overtime = 0
IF #fromSeconds < 32400 -- 32400 seconds = 09:00 hours
SET #Overtime = #OverTime + (32400 - #fromSeconds)
IF #toSeconds > 64800 -- 64800 seconds = 18:00 hours
SET #Overtime = #OverTime + (#toSeconds - 64800)
RETURN #Overtime
END
With those two functions in place, I can fairly easily calculate what you're looking for:
SELECT
dbo.GetOvertime(dbo.GetSeconds(InTime), dbo.GetSeconds(OutTime)) 'Overtime',
(dbo.GetSeconds(OutTime) - dbo.GetSeconds(InTime) -
dbo.GetOvertime(dbo.GetSeconds(InTime), dbo.GetSeconds(OutTime))) 'Worktime',
FROM YourTable
It's a bit involved - as I said, if you'd be on SQL Server 2008 and using the TIME data type, things would be a whole lot easier!
Marc
You could use substring and dateadd to convert the '090000' to a real datetime field. Then you can use CASE and DATEDIFF to split work and overtime. The final formatting can be done with CONVERT. Here's an example:
select
case when outtime-intime > '9:00:00' then '09:00:00'
else convert(varchar(30), outtime-intime, 108)
end as WorkTime,
case when outtime-intime <= '9:00:00' then '00:00:00'
else convert(varchar(30), outtime-intime-'9:00:00', 108)
end as OverTime
from (
select
dateadd(hh,cast(substring('090000',1,2) as int),0) +
dateadd(mi,cast(substring('090000',3,2) as int),0) +
dateadd(ss,cast(substring('090000',5,2) as int),0) as InTime,
dateadd(hh,cast(substring('180500',1,2) as int),0) +
dateadd(mi,cast(substring('180500',3,2) as int),0) +
dateadd(ss,cast(substring('180500',5,2) as int),0) as OutTime
) vw
This will print:
09:00:00 00:05:00
Here is code for SQL server 2000. 2005+ could do it without subquery using cross join.
Select DateAdd(mi, (Case When bef<0 Then bef else 0 end + Case When aft<0 Then aft else 0 end), diff) as WorkTome,
DateAdd(mi, (Case When bef>0 Then bef else 0 end + Case When aft>0 Then aft else 0 end), 0) as OverTime
From (
Select outTime-inTime as diff,
DateDiff(mi,t.inTime,'09:00') as bef,
DateDiff(mi,'18:00',t.outTime) as aft
From Table t ) as a
If Your inTime and outTime columns hase date part too, the query is slightly different:
Select DateAdd(mi, (Case When bef<0 Then bef else 0 end + Case When aft<0 Then aft else 0 end), diff) as WorkTome,
DateAdd(mi, (Case When bef>0 Then bef else 0 end + Case When aft>0 Then aft else 0 end), 0) as OverTime
From (
Select outTime-inTime as diff,
DateDiff(mi, DateAdd(Day,-DateDiff(Day, 0, t.inTime), t.inTime),'09:00') as bef,
DateDiff(mi,'18:00',DateAdd(Day,-DateDiff(Day, 0, t.OutTime), t.OutTime)) as aft
From #t t ) as a
I missed type of inTime and outTime.
Here is the version for varchar
Missed that, but you just have to do Cast(inTime as datetime) .
Use the first of previous 2 queries:
Select DateAdd(mi, (Case When bef<0 Then bef else 0 end + Case When aft<0 Then aft else 0 end), diff) as WorkTome,
DateAdd(mi, (Case When bef>0 Then bef else 0 end + Case When aft>0 Then aft else 0 end), 0) as OverTime
From (
Select Cast(t.outTime as datetime)-Cast(t.inTime as datetime) as diff,
DateDiff(mi,Cast(t.outTime as datetime),'09:00') as bef,
DateDiff(mi,'18:00',Cast(t.outTime as datetime)) as aft
From Table t ) as a

Resources