SQL Server Assign Conditional Variable in Select Statement - sql-server

I am not an SQL expert and need some help in figuring this out. I am writing a payroll application with SQL Server 2012 database that need to keep track of time user punched in , punched out and then calculate work time and other calculations based on work time. Work time is computed as time between 9:30 a.m. and 5:30 p.m. Here is the logic:
if the user punches in earlier than 9:30 a.m., work start time is set
to 9:30 a.m.
If the user punches in later than 9:30 a.m. work start time would be
actual time punched in.
if the user punches out after 5:30 p.m. work end time would be set at
5:30 p.m.
if the user punches out before 5:30 p.m. work end time would be set
to actual punch out time
Here the partial SQL I tried but not getting anywhere. Any help would be appreciated:
declare #start time;
declare #end time;
select #start=cast('09:30:00.0000000' as time)
select #end=cast('17:30:00.0000000' as time)
SELECT Datename(dw,LastUpdate) as WeekDay
,FORMAT(PunchIn,'MM/dd/yyyy hh:mm tt') as PunchIn
,FORMAT(PunchOut,'MM/dd/yyyy hh:mm tt') as PunchOut
,case
--employee punched in before 9:30 so take 9:30 as start time
when datediff(mi,#start,cast(PunchIn as time))<0 then (select #start='09:30:00.0000000') end
from TimeTracker
This is not the full SQL but you can see the logic I am trying to employ. My question is how do I set the start time or end time based on value in another column such as punch in time or punch out time. SQL gives an error in the select statement as part of the when clause. I can do this easily in other languages but getting stumped in SQL.

You were almost there!
Below is your modified SQL code. See https://learn.microsoft.com/en-us/sql/t-sql/language-elements/case-transact-sql for the full syntax of CASE stmt.
declare #start time;
declare #end time;
select #start=cast('09:30:00.0000000' as time)
select #end=cast('17:30:00.0000000' as time)
SELECT Datename(dw,LastUpdate) as WeekDay
, FORMAT(PunchIn,'MM/dd/yyyy hh:mm tt') as PunchIn
, FORMAT(PunchOut,'MM/dd/yyyy hh:mm tt') as PunchOut
, Start = case
--employee punched in before 9:30 so take 9:30 as start time
when datediff(mi,#start,cast(PunchIn as time)) < 0 then #start
else cast(PunchIn as time)
end
, Stop = case
--employee punched out after 17:30 so take 17:30 as end time
when datediff(mi,#end,cast(PunchOut as time)) > 0 then #end
else cast(PunchOut as time)
end
from TimeTracker
NOTE: Your original code contained a reference to variable #start, which is not permitted. "A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations" (actual T-SQL error message). Therefore the solution posted will display result set that consists of five columns (Weekday, PunchIn, PunchOut, Start, Stop). If you were to use variables and more than one record was returned from the query, then only the last record would be saved in these variables! To store the entire set into another table, add additional line
into [table name]
before "FROM" keyword.

CASE statements are used to choose specific value expressions. There must be a specific value result for the statement. CASE statements are not if conditionals... they will not evaluate to executable code.
So instead of this:
--Bad
CASE WHEN 1=0 THEN Select #x=4+#y ELSE Select #x=2+#y END
You have to do things like this:
--Better
SELECT #x = CASE WHEN 1=0 THEN 4+#y ELSE 2+#y END
This shows you can still put some code in the expression, but it must still evaluate down to a value.
For the question here, it should look like more this:
case
--employee punched in before 9:30 so take 9:30 as start time
when datediff(mi,#start,cast(PunchIn as time))<0 then '09:30:00.0000000'
else PunchIn end
Though I still question doing an assignment in here.
This code is also interesting because, in most jurisdictions I've seen, it is highly illegal to alter PunchIn times like this. If an employee clocks in before they are supposed to that can be a disciplinary issue (management will ask them not to come in early, start writing them up, eventually fire them, etc), but until that process catches up the law says you must pay them for the time they worked. Again, this is jurisdictional, so consult a lawyer, but it sure looks like stealing time from workers.

Related

T-SQL stored procedure timing out on assigning query results to a parameter

I have a stored procedure that has been running well for a few weeks. It does a lot and typically takes 10 seconds to run.
There have been no changes, but now it times out.
I've simulated the code where it chokes below. It creates a parameter, assigns it, and prints it. It takes over a minute sometimes:
declare #myDec decimal(10,2);
set #myDec = (select sum(Hours)
from VATT_Time_Entries_With_Costs_For_Invoicing
where date >= getDate() - 110 and date <= getDate() - 18.75) -- /3
The exact same query, just not assigning it to a parameter, runs in under a second:
select sum(Hours)
from VATT_Time_Entries_With_Costs_For_Invoicing
where date >= getDate() - 110 and date <= getDate() - 18.75
I'm perplexed as I've written hundreds of lines of code that work like this without issues.
Removing the date functions getDate()-110 from the query and putting them as parameters, and setting them above running the query made a major improvement (from one minute to less than a second). I'm still not clear why running the query on its own vs. assigning to a parameter made such a difference. Thanks to all commentators.

How to set SSRS subscription time as an expression

I have a report and the report StartDate and EndDate parameters are using the expression as a default value below.
=DateAdd(DateInterval.Minute,0,DateAdd("h",7,DateAdd("h",-24,Today())))
=DateAdd(DateInterval.Minute,0,DateAdd("h",7,Today()))
When I execute the report, the report is starting from the day before at 7 AM to today 7 AM.
I would like to keep the report Start time and End time like this(07:00).
I also want to send the report to customer every day 7:30 AM but the report needs to be executed according to start date and end date paramaters.
Example: today 12.12.2019
Subscription time will be 07:30 AM
report needs to be running this time:
StartDate : 11/12/2019 07:00:00
EndDate : 12/12/2019 07:00:00
But when I schedule subscription every day and 7:30 AM, I received report from one day before 7:30 AM and today 7:30 AM.
I just want to see report from 7:00am to 7 am. Even if I change schedule time.
Could you please help me about this problem. How can I edit my subscription?
Is it possible to write an expression in "date/time from - date/time to" fields in subscription?
Btw, When I unclick “use Default” part, it always takes 11-12-2019 even 2 days after ☹
Time from needs to be one day before at 07:00 AM
Time to should be on that day at 07:00 AM
Do you have any suggestion for it?
Thanks
I resolved my issue. There are 2 solutions for it.
Option 1 :
In the report design If it is must to have those date parameters must be DATTEIME and to allow TIME factor as well then and if you want to run the report which is subscribed always for Yesterday 7:00 to today 7:00 am then I would not rely on sending any parameter values based on expressions …I would set up Date/Time Parameter in report design to allow null values and send null values as default from the subscription settings.
Then In report SP you can always add a clause at the TOP like
if #startDateTime is null AND #endDateTime is null
begin
set #startDateTime =CONVERT(VARCHAR(10), getdate(), 111);
set #startDateTime =dateadd(hh,7,( dateadd(d,-1,#startDateTime)))
set #endDateTime =dateadd(d,1,#startDateTime)
end
and let the rest SP be same
Option 2 :
If you can change the report parameters to be a type only DATE then its easy always send =Today() in your subscription parameter for both Start & End
Then In report SP you can always add a clause at the TOP like
if #startDateTime = #endDateTime
begin
set #endDateTime =CONVERT(VARCHAR(10), #endDateTime, 111);
set #endDateTime =dateadd(hh,7,#endDateTime)
set #startDateTime =dateadd(d,-1,#startDateTime)
end
and let the rest SP be same
Option 2 is better if they are ok to have Start & End date parameter as just DATE instead of DATETIME.
Any way Using any of these options do handle this in SP… you can always have control in future if they want to change time form 7:00 am to any other time …no need to change report design just update SP…2 minutes
You can schedule this report for any time of the day and it will always send them a report for Yesterday 7:00 to Today 7:00

(T-SQL) Did daylight savings occur in the last hour?

I have a process that is going to use table partitioning (each partition is 1 hour) and I need to handle the daylight savings flips when archiving out the data.
For instance, this past weekend it went from 1:59:59 to 1:00:00, so the second time that the partitioning code ran at 1:05am nothing would occur - the midnight hour had already been SWITCHed out.
However, when spring rolls around, the time goes from 1:59am to 3:00am, so when the job runs at 3:05, it would SWITCH out 2am.... leaving the data from the 1am hour in the original table.
In theory I can just look for the oldest not-current partition with data and flip that one (partition key is a default getdate() constraint), but I was wondering if there was some way to use AT TIME ZONE to determine that daylight savings had "occurred", so that we could have different code to handle that older hour still being out there.
Thanks.
Maybe something like this. Basically take your current getdate() and use AT TIME ZONE with any time zone that observes. Then use DATEPART tz and compare your current and before.
Regardless of what the server time zone is, using AT TIME ZONE will get you the offset for a particular datetime value in that time zone.
For comparison of before I think you'd need to use 2 hours, 1 for the switch and 1 for how far back you want to check.
Give this a look:
DECLARE #BeforeDate DATETIME = '2018-11-04 1:59' --Before the change
DECLARE #AfterDate DATETIME = '2018-11-04 3:00' --After the change
--use can use AT TIME ZONE with DATEPART tz which tells you offset in minutes
--I'm in central, any should work for any that observe the time change
--Your offset is different because of the change.
SELECT DATEPART(tz, #BeforeDate AT TIME ZONE 'Central Standard Time')
SELECT DATEPART(tz, #AfterDate AT TIME ZONE 'Central Standard Time')
--using the above you could possibly compare current offset to 2 hours prior to see if they changed. 2 hours, 1 for the switch and 1 for how far back you want to compare.
DECLARE #CurrentDate DATETIME = '2018-11-04 3:00' --"simulate" getdate() as of the time change
DECLARE #PriorOffSet INT = (SELECT DATEPART(tz, DATEADD(HOUR, -2, #CurrentDate) AT TIME ZONE 'Central Standard Time')) --You'd have to subtract 2 hours to account for the hour shift and the hour back you want to check.
DECLARE #CurrentOffset INT = (SELECT DATEPART(tz, #CurrentDate AT TIME ZONE 'Central Standard Time'))
SELECT #PriorOffSet, #CurrentOffset
IF #PriorOffSet <> #CurrentOffset
SELECT 'Time changed in the last hour'
ELSE
SELECT 'No time change in the last hour'

Dealing with out of range value on varchar date conversion

I'm attempting to convert dates input in our system as text in the format YYYYMMDD into dates. Unfortunately our system allows the use of the 31st of any month to signify that it's the last day of the month that's important, for some functions like interest accrual etc.
I have a date showing as 20160931 which obviously fails to convert via
CONVERT(DATETIME, CONVERT(CHAR(8), [FIELD]))
and throws the out-of-range value error.
How can I overcome this, so that I can convert it to the correct value, in this case 30/09/2016?
Adapting #Shnugo's technic, I feel it's better to leave SQL to decide the end of month. Hence:
SELECT eomonth(CAST(y+m+'01' AS DATE))
FROM (VALUES(LEFT(#YourDate,4)
,SUBSTRING(#YourDate,5,2)
,SUBSTRING(#YourDate,7,2))) AS Parts(y,m,d)
Will give '2016-02-29' instead of '2016-02-28' with a constant '28' for Feb.
You might try something like this:
DECLARE #YourDate VARCHAR(100)='20160231';
SELECT CAST(y+m+dNew AS DATE)
FROM (VALUES(LEFT(#YourDate,4)
,SUBSTRING(#YourDate,5,2)
,SUBSTRING(#YourDate,7,2))) AS Parts(y,m,d)
CROSS APPLY
(
SELECT CASE WHEN CAST(m AS INT) IN(4,6,9,11) AND CAST(d AS INT)>30 THEN '30'
ELSE CASE WHEN CAST(m AS INT)=2 AND CAST(d AS INT)>28 THEN '28' ELSE d END
END AS dNew
) AS NewDay
And about a 29th of February you just have - additionally - to check if the year is to be divided by 4 :-)
UPDATE
Now it's on me to evolve #Irawan's technique :-)
Since SQL Server 2005 has not got the EOMONTH function, but it is surely better to let SQL Server do the calculation (29th of Feb implicitly solved!), I'd suggest this:
DECLARE #YourDate VARCHAR(100)='20160231';
SELECT DATEADD(SECOND,-1,DATEADD(MONTH,1,CAST(y+m+'01' AS DATETIME)))
FROM (VALUES(LEFT(#YourDate,4)
,SUBSTRING(#YourDate,5,2)
,SUBSTRING(#YourDate,7,2))) AS Parts(y,m,d)
This will - in any case - deliver the last second of the month...
If you want a plain DATE (without a time), you might just change the SECOND to DAY which will first jump to midnight of the first day of the next month and than go one day back...
UPDATE 2 Use existing dates if valid
Simple syntax...
DECLARE #YourDate VARCHAR(100)='20160229';
SELECT CASE WHEN ISDATE(#YourDate)=1 THEN #YourDate
ELSE DATEADD(SECOND,-1,DATEADD(MONTH,1,CAST(LEFT(#YourDate,4) + SUBSTRING(#YourDate,5,2) +'01' AS DATETIME)))
END AS CorrectDate;

t-SQL Selecting a records where an event happened today accounting for midnight and timezones

I have a table with a date column and time column and it gets populated whenever the user performs a scheduled task. They generally have a 2hr window to perform the task and I can't seem to get my mind around how to see if that task was done today or not.
For example, if it is 12:30 AM, I need to check whether it was done at 11:30 PM on the prior day, or at 12:01 AM today. I store all the time and dates as central time and have an offset number to adjust for other timezones.
Here is where I'm stuck, this works for something done last night, but not if it was done today after midnight (#DatePartOfDateTime is the date I am checking (it has already been corrected for timezone) and #EndTime is the time I am checking to see if it happened):
select 1 from tblMedsDispensed
where DatePassed =
case when (#EndTime = '23:59' or #Offset < 0 and #EndTime < '21:59')
and TimePassed < '01:00'
-- For midnight, if the time passed is < 01:00 then roll it back a day
then dateadd(DAY,1,#DatePartOfDateTime )
else #DatePartOfDateTime end
and tblMedsDispensed.patdrugs_fk = tblPatDrugs.PatDrugs_id
Any ideas?
Here's a guess. I don't think you need to mess around with all those variables and time components. What you want to do is sort of rotate the clock so that your logical start of day lines up with a calendar day so the date comparisons are easy.
select *
from tblMedsDispensed
where
cast(dateadd(hh, #Offset - 1, DatePassed) as date) = cast('<literal date>' as date)
EDIT: I looked over your question again and I see you've got two columns. The idea here is the adjust the date value backward one day when the hour component is less than 1. I'm still not clear whether your tolerance is one hour or two hours.
dateadd(
hh,
case when hour(dateadd(hh, #Offset, <TimeColumn>)) < 1 then -1 else 0 end,
<DateColumn>
)

Resources