Modify Date Function Based on Where Clause SQL Server - sql-server

Currently my case statement is based on the first day of the current month.
SELECT
[TicketNbr] AS 'Ticket Nbr'
-- 1st day of this month
, ( CASE WHEN [date_entered] >= DATEADD(day,-1, GETDATE()) THEN 1 ELSE 0 END) AS '1=Opened Within Last 24HR 0=No'
FROM [v_rpt_Service] WITH(NOLOCK)
WHERE ([date_entered] >= '2017-04-01T11:24:00.000' AND [date_entered] < '2017-05-01T11:24:00.000')
GROUP BY [TicketNbr]
But I want to modify it so it will adjust so it will based on the date range
in the where clause. (And that can be any date ranges.)
How do I do this without variables?

Sounds like you just need variables
declare #startDate datetime = '2017-04-01 11:24:00'
declare #endDate datetime = '2017-05-01 11:24:00'
SELECT
[TicketNbr] AS 'Ticket Nbr'
-- 1st day of this month
, ( CASE WHEN [date_entered] between #startDate and #endDate THEN 1 ELSE 0 END) AS '1=Opened Within Date Range 0= No'
FROM [v_rpt_Service] WITH(NOLOCK)
WHERE ([date_entered] >= startDate AND [date_entered] < #endDate)
GROUP BY [TicketNbr]

Related

Count number of occurrences of a day between two dates SQL Server

In SQL Server, how many 8 day periods do I have between this two dates?
#date1 = '2016/11/08'
#date2 = '2017/02/10'
Manually I have four 8 day periods between those dates
Declare #date1 date = '2016/11/08'
Declare #date2 date = '2017/02/10'
Select count(*)
From (
Select Top (DateDiff(DD,#date1,#date2)+1) D=cast(DateAdd(DD,Row_Number() Over (Order By (Select null))-1,#date1) as Date)
From master..spt_values n1, master..spt_values n2
) A
Where DatePart(DAY,D)=8
Returns
4
Here's an alternate approach using the standard SQL Date functions:
declare #StartDate datetime = '2016-11-08'
, #EndDate datetime = '2023-10-09' --'2017-02-10'
, #dayOfInterest int = 8 --in case you decided you were interested in a different date
--if you don't care whether or not start date is before or after end date; swap their values...
/*
select #StartDate = min(d)
, #EndDate = max(d)
from (select #StartDate d union select #EndDate) x
*/
select
case when #StartDate <= #EndDate then
--if the end date is after or on the start date, we count the number of eighths in between
datediff(month,#StartDate,#EndDate) --count the number of months between the two dates
+ case when datepart(dd,#StartDate)<=#dayOfInterest then 0 else -1 end --if our range excludes the nth this month, exclude it
+ case when datepart(dd,#EndDate)<#dayOfInterest then 0 else 1 end --if our range includes the nth on the last month, include it
else
0 --if the end date is before the start date; we return 0
end
CountTheDays --give the output column a name
Caution
NB: For the 8th this will work perfectly.
For dates after 28th it isn't reliable (you'd be better off going with John Cappelletti's solution), as my logic would add 1 day per month, regardless of whether that month contained that day (i.e. if counting the number of 30ths between 1st Jan and 1st March you'd get 2 instead of 1, as my logic assumes Feb 30th to be a valid date.

NetWorkDays Function in SQL SERVER 2012

Is there a function in sql server 2012 which calculates only working days?
I have been searching but with no luck so far.
Thanks!
No, SQL Server doesn't have such functions, but you can use calendar table:
DECLARE #date_start date = '2016-01-01',
#date_end date = '2016-12-31';
WITH cte as (
SELECT #date_start as [d], 0 as Level
UNION ALL
SELECT DATEADD(day,1,[d]), [level] + 1 as [level]
from cte
WHERE [level] < DATEDIFF(day,#date_start,#date_end)
),
holidays as ( --table with holidays (USA)
SELECT * FROM (VALUES
('2016-01-01'),
('2016-01-18'),
('2016-02-15'),
('2016-05-30'),
('2016-07-04'),
('2016-09-05'),
('2016-10-10'),
('2016-11-11'),
('2016-11-24'),
('2016-12-26')) as t(d)
)
SELECT c.d,
CASE WHEN DATEPART(WEEKDAY,c.d) IN (1,7) THEN 0 --Saturday and Sunday, use (6,7) for Friday,Saturday
WHEN h.d IS NOT NULL THEN 0
ELSE 1 END as isWorking
FROM cte c
LEFT JOIN holidays h
ON c.d=h.d
OPTION (MAXRECURSION 1000);
It will generate a table with all dates in 2016 year and flag - is the day working or not.
Below is the high level overview of how you can do this..
Create a dummy table which holds dates in this format...
date isholiday
20160101 1
20160102 0
Now from your main table which holds employees attendance ,join above table like..
select empid,sum(Case when mt.datee is not null then 1 else 0 end) as workingdays
from
dummydatees dt
left join
maintable mt
on dt.datee=mt.datee
where dt.isholiday=0
This script will calculate the total working days excluding Saturday, Sunday and holidays. I have to list all the holidays since I don't have a table for holidays. You can modify it so that it will meet your requirements.
DECLARE #MyCounter int = 0, #TempDate datetime, #EndDate datetime;
SET #TempDate = DATEADD(d,1,'2017-5-27')
SET #EndDate = '2017-6-3'
WHILE #TempDate <= #EndDate
BEGIN
IF DATENAME(DW,#TempDate) = 'Sunday' OR DATENAME(DW,#TempDate) = 'Saturday'
SET #MyCounter = #MyCounter
ELSE IF #TempDate not in ('2017-1-1', '2017-1-16', '2017-2-20', '2017-5-29', '2017-7-4', '2017-9-4', '2017-10-9', '2017-11-11', '2017-12-25')
SET #MyCounter = #MyCounter + 1
SET #TempDate = DATEADD(d,1,#TempDate)
CONTINUE
END
PRINT #MyCounter
PRINT #TempDate

SQL Date Range between a week by week range

we are making a date range picker for employee vacations
for example i will user January 2016 as an example and our system goes from sunday through saturday
if an employee takes a vacation from January 14-20 they should be in the date range 10-16 and also 17-23
I just cant think of how to write an sql query that will limit it to those 2 dates.
What i have is:
DECLARE #WeekRangeStart DATETIME ='2016/01/10';
DECLARE #WeekRangeEnd DATETIME = '2016/01/16';
SELECT [ID],
[EmpName],
[EmpType]
FROM Vacations
WHERE VacationStartDate >= #WeekRangeStart OR VacationEndDate >= #WeekRangeStart
--OUTPUT
--ALL DAYS BEFORE THIS WOULD BE TRUE...
--1/14/2016 >= 1/10/2016 TRUE
--1/20/2016 >= 1/10/2016 TRUE
-- NEXT WEEK
--1/14/2016 >= 1/17/2016 FALSE
--1/20/2016 >= 1/17/2016 TRUE
-- NEXT WEEK
--1/14/2016 >= 1/24/2016 FALSE
--1/20/2016 >= 1/24/2016 FALSE
--ALL DAYS AFTER THIS DAY WOULD BE FALSE...
but this only works for for a things that have passed, But if i were to book a day in march i would always show on the schedules because my startdate would greater than today. How should i go about limiting it to that range only?
DECLARE #WeekRangeStart DATETIME ='2016/01/10';
DECLARE #WeekRangeEnd DATETIME = '2016/01/16';
SELECT [ID],
[EmpName],
[EmpType]
FROM Vacations
WHERE VacationStartDate between #WeekRangeStart and #WeekRangeEnd
or VacationEndDate Between #WeekRangeStart and #WeekRangeEnd
This might be useful. Using a recursive CTE I got all the dates the vacations spans. Then return the weeks it spans based on the week ranges provided.
DECLARE #WeekRangeStart DATETIME ='2016/01/10';
DECLARE #WeekRangeEnd DATETIME = '2016/01/16';
DECLARE #VacationStartDate DATETIME = '2016-01-14'
DECLARE #VacationEndDate DATETIME = '2016-01-20'
;WITH cte AS
(
SELECT #VacationStartDate AS Dates
UNION ALL
SELECT DATEADD(dd, 1, dates)
FROM cte
WHERE dates < #VacationEndDate
)
SELECT DISTINCT
DATEADD(dd, -(DATEPART(dw, dates)-1), dates) AS WeekStartDate,
DATEADD(dd, 7-(DATEPART(dw, dates)), dates) AS WeekEndDate
FROM cte
WHERE dates BETWEEN #WeekRangeStart AND #WeekRangeEnd

SQL : If first day of the new month then run report for previous month

I have some reports which run showing data MTD
Here is the code that is not working how I would like
StartDate = select dateadd(s,0,dateadd(mm, datediff(m,0,getdate()),0))
EndDate = getdate()
Our data replication happens at the end of each day.
So on the First day of each month I don't want a blank report to run.
what I would like to happen.
Only If its the first day of the month then the StartDate must be beginning of last month and EndDate to be end of last month. Else use
StartDate = select dateadd(s,0,dateadd(mm, datediff(m,0,getdate()),0)) and
EndDate = getdate()
Not exactly sure about your main query. This is how you could get first and last day of last month depending on the given date being 1st of current month.
Please note, else part of each case expression setting the current date for both first and last date. You can set them as null if needed.
DECLARE #Today DATETIME = GETDATE()
DECLARE #FirsDay DATETIME = CASE WHEN DATEPART(DAY, #Today) = 1
THEN DATEADD(MONTH, -1, #Today) --first day of last month
ELSE #Today END --current date for other dates
DECLARE #LastDay DATETIME = CASE WHEN DATEPART(DAY, #Today) = 1
THEN DATEADD(DAY, -1, #Today) --last date of last month
ELSE #Today END --current date for other dates
Thanks all.
so the rabbit hole got a bit deeper
Example : What about when the 1st falls on a Saturday ?
I need it to run using the last trading day if its the first day of the new month.
what I ended up using was a function that uses our working hours table
ALTER FUNCTION [data].[Last_Trade_Day] (#Date date)
returns date as
begin
declare #OrigDate date = isnull(#Date, getdate())
return (
select max(convert(date, wh_starttime))
from Embrace.fact.Working_Hours
where convert(date, wh_starttime) < #OrigDate
)
end
The code in the report now look's like :
declare #EndDate date = Embrace.data.Last_Trade_Day(isnull(#Date, getdate()))
declare #StartDate date = dateadd(mm, 0, dateadd(mm, datediff(m, 0, #EndDate), 0))
You can try to use something like this:
-- Create demo data
CREATE TABLE #dates(get_date_simulation datetime)
INSERT INTO #dates(get_date_simulation)
VALUES (N'2015-07-01 13:46:47.063'), -- fallback to 2015-06-01
(N'2015-07-02 13:46:47.063') -- use this date normal
-- Your part
SELECT get_date_simulation,
CASE
WHEN DATEPART(day,get_date_simulation) = 1
THEN DATEADD(month,-1,DATEADD(day,(DATEPART(day,get_date_simulation)-1)*-1,get_date_simulation))
ELSE DATEADD(day,(DATEPART(day,get_date_simulation)-1)*-1, get_date_simulation)
END as start_date,
CASE
WHEN DATEPART(day,get_date_simulation) = 1
THEN DATEADD(second,-1,CONVERT(datetime,CONVERT(date,get_date_simulation)))
ELSE get_date_simulation
END as end_date
FROM #dates
-- Cleanup
DROP TABLE #dates
Which results into this:
get_date_simulation start_date end_date
----------------------- ----------------------- -----------------------
2015-07-01 13:46:47.063 2015-06-01 13:46:47.063 2015-06-30 23:59:59.000
2015-07-02 13:46:47.063 2015-07-01 13:46:47.063 2015-07-02 13:46:47.063
To me it seems as simple as subtracting 1 day from the current date to get the start date
SELECT StartDate =
DATEADD(s,0,DATEADD(mm,DATEDIFF(m,0,GETDATE() - 1),0))
Then you just get the end date using the current date without the time
SELECT EndDate =
CONVERT(DATE, GETDATE()
Then your query is WHERE date >= StartDate and < EndDate

Checking a range of dates in SQL

Following on from a question I put on yesterday, I need to return a range of "available" dates for a laptop rollout "booking system". I want to populate a table of possible available dates a user can book a slot on by checking for each date the total possible number of slots, and subtracting the number of slots already booked.
The logic is as follows:
A technician can build 3 laptops per day.
On any day there may be 1, 2 or 3 technicians available.
A table holds the bookings made
I don't want a table of all possible dates, I want to calculate it on the fly
Relevant tables are:
tl_sb_slotBooking
This contains the bookings already made
tl_sb_availabilityPeriods
This is used to calculate the total number of available slots on a given day
I can bring back a list of dates with a fixed maximum number (in this case 3) of slots:
DECLARE #startDate DATE
DECLARE #endDate DATE
SET #startDate = GETDATE()
SET #endDate = DATEADD(m,3,#startDate)
;
WITH dates(Date) AS
(
SELECT #startdate as Date
UNION ALL
SELECT DATEADD(d,1,[Date])
FROM dates
WHERE DATE < #enddate
)
SELECT Date
FROM dates
EXCEPT
SELECT date
FROM tl_sb_booking
GROUP BY date
HAVING COUNT(date) >= 3
However, the maximum won't always be 3, it changes for each day.
I can find the maximum possible slots for a given day:
DECLARE #myDate DATETIME = '2013-06-22'
SELECT SUM(laptopsPerDay) AS totalSlots
FROM tl_sb_technicianAvailability
WHERE startDate <= #myDate AND endDate >= #myDate
AND availabiltyStateID=3
it will bring back 6 as the total number of slots available for 2013-06-22. (The availabilityStateID field is used to store available/unavailable etc.)
So, the bit I am stuck on is combining the two.
What I want is for each possible date, if the number of slots already booked is less than the number of possible slots for that day, add it to the table being returned (otherwise don't).
Firstly, althought you are only generating a small list, using a CTE to generate a sequential list performs terribly and is best avoided.
For the sake of this I will use the system table Master..spt_values for a sequential list of numbers, but if you are worried about using undocumented system tables then there are other methods in the link above.
The first thing I would do is split the technician availability dates into a row per day, this will allow for technicians who are available for only part of the peiod required (e.g. from your sceen shot of the table if you wanted to query from 18th June to 26th June none of the technicians show would appear as available using the query you have posted):
SELECT Date = DATEADD(DAY, spt.Number, ta.StartDate),
ta.TechnicianID,
ta.LapTopsPerDay
FROM tl_sb_technicianAvailability ta
INNER JOIN Master..spt_values spt
ON spt.Type = 'P'
AND spt.Number BETWEEN 0 AND DATEDIFF(DAY, ta.startDate, ta.EndDate)
This would simply turn:
TechnicianID StartDate EndDate LapTopsPerDay
1 20130620 20130624 3
into
Date TechnicianID LapTopsPerDay
20130620 1 3
20130621 1 3
20130622 1 3
20130623 1 3
20130624 1 3
You can then limit this list to the date range required, and sum up the total laptops than can be done as this is not needed on a technicial level:
WITH ExplodedAvailability AS
( SELECT Date = DATEADD(DAY, spt.Number, ta.StartDate),
ta.TechnicianID,
ta.LapTopsPerDay
FROM tl_sb_technicianAvailability ta
INNER JOIN Master..spt_values spt
ON spt.Type = 'P'
AND spt.Number BETWEEN 0 AND DATEDIFF(DAY, ta.startDate, ta.EndDate)
)
SELECT Date, TotalLaptops = SUM(LapTopsPerDay)
FROM ExplodedAvailability
WHERE Date >= #StartDate
AND Date < #EndDate
GROUP BY Date;
Finally you can LEFT JOIN to the bookings table to get the available slots per day
WITH ExplodedAvailability AS
( SELECT Date = DATEADD(DAY, spt.Number, ta.StartDate),
ta.TechnicianID,
ta.LapTopsPerDay
FROM tl_sb_technicianAvailability ta
INNER JOIN Master..spt_values spt
ON spt.Type = 'P'
AND spt.Number BETWEEN 0 AND DATEDIFF(DAY, ta.startDate, ta.EndDate)
), Availability AS
( SELECT Date, TotalLaptops = SUM(LapTopsPerDay)
FROM ExplodedAvailability
WHERE Date >= #StartDate
AND Date < #EndDate
GROUP BY Date
), Bookings AS
( SELECT Date, SlotsBooked = COUNT(*)
FROM tl_sb_booking
GROUP BY Date
)
SELECT Availability.Date,
Availability.TotalLaptops,
RemainingSlots = Availability.TotalLaptops - ISNULL(Bookings.SlotsBooked, 0)
FROM Availability
LEFT JOIN Bookings
ON Bookings.Date = Availability.Date;
I think what you are after is to add a booking to the next available day, so the query to do this would be:
DECLARE #UserID INT = 1;
WITH ExplodedAvailability AS
( SELECT Date = DATEADD(DAY, spt.Number, ta.StartDate),
ta.TechnicianID,
ta.LapTopsPerDay
FROM tl_sb_technicianAvailability ta
INNER JOIN Master..spt_values spt
ON spt.Type = 'P'
AND spt.Number BETWEEN 0 AND DATEDIFF(DAY, ta.startDate, ta.EndDate)
), Availability AS
( SELECT Date, TotalLaptops = SUM(LapTopsPerDay)
FROM ExplodedAvailability
WHERE Date >= CAST(GETDATE() AS DATE)
GROUP BY Date
), Bookings AS
( SELECT Date, SlotsBooked = COUNT(*)
FROM tl_sb_booking
GROUP BY Date
)
INSERT tl_sb_slotBooking (UserID, Date)
SELECT #UserID, MIN(Availability.Date)
FROM Availability
LEFT JOIN Bookings
ON Bookings.Date = Availability.Date
WHERE Availability.TotalLaptops > ISNULL(Bookings.SlotsBooked, 0)
Should this be of use to anyone, this is the way I ultimately did it:
DECLARE #startDate DATE
DECLARE #endDate DATE
SET #startDate = GETDATE()
SET #endDate = DATEADD(m,3,#startDate)
;
WITH dates(currentDate) AS
(
SELECT #startdate as currentDate
UNION ALL
SELECT DATEADD(d,1,[currentDate])
FROM dates
WHERE currentDate < #enddate
)
SELECT currentDate
FROM dates
WHERE /* slots booked for date */
(
SELECT count([date])
FROM tl_sb_booking
where [date] = currentDate
)
<
/* total slots available */
(
SELECT SUM(laptopsPerDay) AS totalSlots
FROM tl_sb_technicianAvailability
WHERE startDate <= currentDate AND endDate >= currentDate
AND availabiltyStateID=3
)

Resources