MSSQL - Getting last 6 weeks returns last 8 weeks - sql

I am having a problem with week numbers. The customers week starts on a Tuesday, so ends on a Monday. So I have done:
Set DateFirst 2
When I then use
DateAdd(ww,#WeeksToShow, Date)
It occasionally gives me 8 weeks of information. I think it is because it goes over to the previous year, but I am not sure how to fix it.
If I do:
(DatePart(dy,Date) / 7) - #WeeksToShow
Then it works better, but obviously doesn't work going through to previous years as it just goes to minus figures.
Edit:
My currently SQL (If it helps at all without any data)
Set DateFirst 2
Select
DATEPART(yyyy,SessionDate) as YearNo,
DATEPART(ww,SessionDate) as WeekNo,
DATEADD(DAY, 1 - DATEPART(WEEKDAY, SessionDate + SessionTime), CAST(SessionDate +SessionTime AS DATE)) [WeekStart],
DATEADD(DAY, 7 - DATEPART(WEEKDAY, SessionDate + SessionTime), CAST(SessionDate + SessionTime AS DATE)) [WeekEnd],
DateName(dw,DATEADD(DAY, 7 - DATEPART(WEEKDAY, SessionDate + SessionTime), CAST(SessionDate + SessionTime AS DATE))) as WeekEndName,
Case when #ConsolidateSites = 1 then 0 else SiteNo end as SiteNo,
Case when #ConsolidateSites = 1 then 'All' else CfgSites.Name end as SiteName,
GroupNo,
GroupName,
DeptNo,
DeptName,
SDeptNo,
SDeptName,
PluNo,
PluDescription,
SUM(Qty) as SalesQty,
SUM(Value) as SalesValue
From
PluSalesExtended
Left Join
CfgSites on PluSalesExtended.SiteNo = CfgSites.No
Where
Exists (Select Descendant from DescendantSites where Parent in (#SiteNo) and Descendant = PluSalesExtended.SiteNo)
AND (DATEPART(WW,SessionDate + SessionTime) !=DATEPART(WW,GETDATE()))
AND SessionDate + SessionTime between DATEADD(ww,#NumberOfWeeks * -1,#StartingDate) and #StartingDate
AND TermNo = 0
AND PluEntryType <> 4
Group by
DATEPART(yyyy,SessionDate),
DATEPART(ww,SessionDate),
DATEADD(DAY, 1 - DATEPART(WEEKDAY, SessionDate + SessionTime), CAST(SessionDate +SessionTime AS DATE)),
DATEADD(DAY, 7 - DATEPART(WEEKDAY, SessionDate + SessionTime), CAST(SessionDate + SessionTime AS DATE)),
Case when #ConsolidateSites = 1 then 0 else SiteNo end,
Case when #ConsolidateSites = 1 then 'All' else CfgSites.Name end,
GroupNo,
GroupName,
DeptNo,
DeptName,
SDeptNo,
SDeptName,
PluNo,
PluDescription
order by WeekEnd

There are two issues here, the first is that I suspect you are defining 8 weeks of data as having 8 different values for DATEPART(WEEK, in which case you can replicate the root cause of the issue by looking at what ISO would define as the first week of 2015:
SET DATEFIRST 2;
SELECT Date, Week = DATEPART(WEEK, Date)
FROM (VALUES
('20141229'), ('20141230'), ('20141231'), ('20150101'),
('20150102'), ('20150103'), ('20150104')
) d (Date);
Which gives:
Date Week
-----------------
2014-12-29 52
2014-12-30 53
2014-12-31 53
2015-01-01 1
2015-01-02 1
2015-01-03 1
2015-01-04 1
So although you only have 7 days, you have 3 different week numbers. The problem is that DATEPART(WEEK is quite a simplistic function, and will simply return the number of week boundaries passed since the first day of the year, a better function would be ISO_WEEK since this takes into account year boundaries nicely:
SET DATEFIRST 2;
SELECT Date, Week = DATEPART(ISO_WEEK, Date)
FROM (VALUES
('20141229'), ('20141230'), ('20141231'), ('20150101'),
('20150102'), ('20150103'), ('20150104')
) d (Date);
Which gives:
Date Week
-----------------
2014-12-29 1
2014-12-30 1
2014-12-31 1
2015-01-01 1
2015-01-02 1
2015-01-03 1
2015-01-04 1
The problem is, that this does not take into account that the week starts on Tuesday, since the ISO week runs Monday to Sunday, you could adapt your usage slightly to get the week number of the day before:
SET DATEFIRST 2;
SELECT Date, Week = DATEPART(ISO_WEEK, DATEADD(DAY, -1, Date))
FROM (VALUES
('20141229'), ('20141230'), ('20141231'), ('20150101'),
('20150102'), ('20150103'), ('20150104')
) d (Date);
Which would give:
Date Week
-----------------
2014-12-29 52
2014-12-30 1
2014-12-31 1
2015-01-01 1
2015-01-02 1
2015-01-03 1
2015-01-04 1
So Monday the 29th December is now recognized as the previous week. The problem is that there is no ISO_YEAR built in function, so you will need to define your own. This is a fairly trivial function, even so I almost never create scalar functions because they perform terribly, instead I use an inline table valued function, so for this I would use:
CREATE FUNCTION dbo.ISOYear (#Date DATETIME)
RETURNS TABLE
AS
RETURN
( SELECT IsoYear = DATEPART(YEAR, #Date) +
CASE
-- Special cases: Jan 1-3 may belong to the previous year
WHEN (DATEPART(MONTH, #Date) = 1 AND DATEPART(ISO_WEEK, #Date) > 50) THEN -1
-- Special case: Dec 29-31 may belong to the next year
WHEN (DATEPART(MONTH, #Date) = 12 AND DATEPART(ISO_WEEK, #Date) < 45) THEN 1
ELSE 0
END
);
Which just requires a subquery to be used, but the extra typing is worth it in terms of performance:
SET DATEFIRST 2;
SELECT Date,
Week = DATEPART(ISO_WEEK, DATEADD(DAY, -1, Date)),
Year = (SELECT ISOYear FROM dbo.ISOYear(DATEADD(DAY, -1, Date)))
FROM (VALUES
('20141229'), ('20141230'), ('20141231'), ('20150101'),
('20150102'), ('20150103'), ('20150104')
) d (Date);
Or you can use CROSS APPLY:
SET DATEFIRST 2;
SELECT Date,
Week = DATEPART(ISO_WEEK, DATEADD(DAY, -1, Date)),
Year = y.ISOYear
FROM (VALUES
('20141229'), ('20141230'), ('20141231'), ('20150101'),
('20150102'), ('20150103'), ('20150104')
) d (Date)
CROSS APPLY dbo.ISOYear(d.Date) y;
Which gives:
Date Week Year
---------------------------
2014-12-29 52 2014
2014-12-30 1 2015
2014-12-31 1 2015
2015-01-01 1 2015
2015-01-02 1 2015
2015-01-03 1 2015
2015-01-04 1 2015
Even with this method, by simply getting a date 6 weeks ago you sill still end up with 7 weeks if the date you are using is not a Tuesday, because you will have 5 full weeks, and a part week at the start and a part week at the end, this is the second issue. So you need to make sure your start date is a Tuesday. The following will get you Tuesday of 7 weeks ago:
SELECT CAST(DATEADD(DAY, 1 - DATEPART(WEEKDAY, GETDATE()), DATEADD(WEEK, -6, GETDATE())) AS DATE);
The logic of this is explained better in this answer, the following is the part that will get the start of the week (based on your datefirst settings):
SELECT DATEADD(DAY, 1 - DATEPART(WEEKDAY, GETDATE()), GETDATE());
Then all I have done is substitute the second GETDATE() with DATEADD(WEEK, -6, GETDATE()) so that it is getting the start of the week 6 weeks ago, then there is just a cast to date to remove the time element from it.

This will get you current week + 5 previous weeks starting tuesday:
WHERE dateadd(week, datediff(d, 0, getdate()-1)/7 - 4, 1) <= yourdatecolumn
This will show examples:
DECLARE #wks int = 6 -- Weeks To Show
SELECT
dateadd(week, datediff(d, 0, getdate()-1)/7 - 4, 1) tuesday5weeksago,
dateadd(week, datediff(d, 0, getdate()-1)/7 - 5, 1) tuesday6weeksago,
dateadd(week, datediff(d, 0, getdate()-1)/7 - 6, 1) tuesday7weeksago,
dateadd(week, datediff(d, 0, getdate()-1)/7 - #wks + 1, 1) tuesdaydynamicweeksago
Result:
tuesday5weeksago tuesday6weeksago tuesday7weeksago tuesdaydynamicweeksago
2015-01-27 2015-01-20 2015-01-13 2015-01-20

Related

How to run different date ranges for different months in SQL

I have a requirement to automate dates for a report. The user runs the report four times a year.
Q1-jan-march- run may1
Q2-Apr-Jun - run Aug1
Q3-July-Sep - run Oct1
Q4-oct-dec - run feb1
when the user runs the query in May he should get the result for SELECT * FROM Table where DATE Between jan1 and march31. How do I write different date ranges according to the month it is being run.
Thanks In advance.
You seem to want to filter on the previous quarter. Here is one way to do it:
select *
from mytable
where date >= dateadd(qq, datediff(qq, 0, getdate()) - 1, 0)
and date < dateadd(qq, datediff(qq, 0, getdate()), 0)
This dynamically computes the beginning and end of the previous quarter based on the current date.
If the query subtracts 2 months from the current month, then the functions to determine the beginning and end of the quarter will always return the correct date range. Something like this
with example_run_dates as (
select cast(v.dt as date) dt
from (values ('20200501'), ('20200531'), ('20200801'), ('20200831'),
('20201001'), ('20201031'), ('20210201'), ('20210228')) v(dt))
select cast(dateadd(qq, datediff(qq, 0, mos.two_ago), 0) as date) start_dt,
cast(dateadd(qq, datediff(qq, 0, mos.two_ago)+1, -1) as date) end_dt
from example_run_dates erd
cross apply (select dateadd(month, -2, erd.dt) two_ago) mos;
Output
start_dt end_dt
2020-01-01 2020-03-31
2020-01-01 2020-03-31
2020-04-01 2020-06-30
2020-04-01 2020-06-30
2020-07-01 2020-09-30
2020-07-01 2020-09-30
2020-10-01 2020-12-31
2020-10-01 2020-12-31
DECLARE #FromDate NVARCHAR(255), #ToDate NVARCHAR(255)
SET #FromDate = CASE
WHEN MONTH(GETDATE()) = 5 THEN '20200101'
WHEN MONTH(GETDATE()) = 8 THEN '20200401'
WHEN MONTH(GETDATE()) = 10 THEN CONCAT(YEAR(GETDATE()),'0701')
WHEN MONTH(GETDATE()) = 2 THEN '20201001'
END
SET #ToDate = CASE
WHEN MONTH(GETDATE()) = 5 THEN '20200330'
WHEN MONTH(GETDATE()) = 8 THEN '20200630'
WHEN MONTH(GETDATE()) = 10 THEN CONCAT(YEAR(GETDATE()),'0930')
WHEN MONTH(GETDATE()) = 2 THEN '20201230' END
select #FromDate, #ToDate

I need data from Prior Monday to saturday when i run my interface on Tuesday at 1:30am. utlized a example query

select *
from VP_TIMESHTPUNCHV42
where (EVENTDATE>=getdate()-8) AND (EVENTDATE <=getdate()-3)
AND PERSONNUM = '668795'
so it should -9 or -8 here? please help me understand about the dates and what dates will be included here ?
If my Tuesday is 15th Oct and the previous Sat is 12thoct and mon is 7thoct
I need data from Mon7th oct to sat12th oct
if you want to get data from Monday to Saturday. WeekDay is from 2(Monday) to 7(Saturday)
select *
from VP_TIMESHTPUNCHV42
where datepart(weekday, EVENTDATE) between 2 and 7 AND PERSONNUM = '668795'
or (if only within this week)
select *
from VP_TIMESHTPUNCHV42
where EVENTDATE between getdate() - 1 and getdate() + 5 AND PERSONNUM = '668795'
select *
from VP_TIMESHTPUNCHV42
where EVENTDATE between dateadd(day, -1, cast(getdate() as date)) and dateadd(day, 5, cast(getdate() as date))
and PERSONNUM = '668795'

need to calculate year,month and day for closing date

I have a table called dates,
Opendate | Closedate
------------+---------------
2015-07-09 | 2016-08-10
I am expecting the output like,
opendate | closedate | diff
------------+---------------+----------------------
2015-07-09 | 2016-08-10 | 1year 1month 1day
2015-07-09 | 2016-03-01 | 8 months 20 days
2015-07-09 | 2015-07-11 | 2 days
But when I run this query:
SELECT opendate,
closedate,
Datediff(year, opendate, closedate) AS years,
Datediff(month, opendate, closedate) AS months,
Datediff(day, opendate, closedate) AS days
FROM dates
It is giving me an output like,
opendate | closedate | years | months | days
------------+---------------+-------+--------+---------
2015-07-09 | 2016-08-10 | 1 | 13 | 397
How can we calculate 1 year 1 month and 1 day
You can use Stacked CTE to find one by one the next year, month and date.
Explanation
Query Below first finds out the DATEDIFF Years of opendate and closedate and checks if the resulting date is greater than closedate. if it is, the actual year difference is DATEDIFF of Y -1. use this new date and fetch the DATEDIFF of months using the same logic and then get the difference in days.
Online Example
Query
WITH D(Opendate,Closedate)AS
(
SELECT CAST('2015-07-09' AS DATE),CAST('2016-08-10' AS DATE)
UNION ALL
SELECT CAST('2015-07-09' AS DATE),CAST('2016-03-01' AS DATE)
UNION ALL
SELECT CAST('2015-07-09' AS DATE),CAST('2015-07-11' AS DATE)
),Y AS
(
SELECT Opendate,Closedate,
CASE
WHEN DATEADD(YEAR,DATEDIFF(YEAR,Opendate,Closedate),Opendate) > Closedate
THEN DATEDIFF(YEAR,Opendate,Closedate) - 1
ELSE DATEDIFF(YEAR,Opendate,Closedate)
END Years
FROM D
), YDate as
(
SELECT Opendate,Closedate,Years,DATEADD(YEAR,Years,Opendate) as Newopendate
FROM Y
),M AS
(
SELECT Opendate,Closedate,Years,Newopendate,
CASE WHEN DATEADD(MONTH,DATEDIFF(MONTH,Newopendate,Closedate),Newopendate) > Closedate
THEN DATEDIFF(MONTH,Newopendate,Closedate) - 1
ELSE DATEDIFF(MONTH,Newopendate,Closedate)
END Months
FROM YDate
)
SELECT Opendate,Closedate,Years,Months,DATEDIFF(Day,DATEADD(MONTH,Months,Newopendate),Closedate) as days
FROM M
Result
Opendate Closedate Years Months days
09-07-2015 00:00 10-08-2016 00:00 1 1 1
09-07-2015 00:00 01-03-2016 00:00 0 7 21
09-07-2015 00:00 11-07-2015 00:00 0 0 2
SELECT opendate,
closedate,
( ( Datediff(year, opendate, closedate) + 'years' )+
(( Datediff(month, opendate, closedate) -
12 * Datediff(year, opendate, closedate)) + 'months') +
( Datediff(day, opendate, closedate) -
( Datediff(year, opendate, closedate) * 365 -
(Datediff(month, opendate, closedate) * 12) )) + 'days'
FROM dates
The logic is you concatenate the years and then deduct the no of months of a year. Similarly deduct for days as well
Create one function as Below
CREATE FUNCTION dbo.GetYearMonthDays
(
#FromDate DATETIME
)
RETURNS NVARCHAR(100)
AS
BEGIN
DECLARE #date datetime, #tmpdate datetime, #years int, #months int, #days int
SELECT #date =#FromDate
SELECT #tmpdate = #date
SELECT #years = DATEDIFF(yy, #tmpdate, GETDATE()) - CASE WHEN (MONTH(#date) > MONTH(GETDATE())) OR (MONTH(#date) = MONTH(GETDATE()) AND DAY(#date) > DAY(GETDATE())) THEN 1 ELSE 0 END
SELECT #tmpdate = DATEADD(yy, #years, #tmpdate)
SELECT #months = DATEDIFF(m, #tmpdate, GETDATE()) - CASE WHEN DAY(#date) > DAY(GETDATE()) THEN 1 ELSE 0 END
SELECT #tmpdate = DATEADD(m, #months, #tmpdate)
SELECT #days = DATEDIFF(d, #tmpdate, GETDATE())
RETURN CONVERT(varchar(10), #years) +' Years ' + CONVERT(varchar(10), #months) + ' Month ' + CONVERT(varchar(10), #days) + ' Days'
END
GO
And use is as below
SELECT opendate,
closedate,dbo.GetYearMonthDays(closedate)
FROM dates
This will give you what you wants.

Query not displaying the week value as 0

SELECT datediff(week, dateadd(week, datediff(week, 0, dateadd(month, datediff(month, 0, EntryDate), 0)), 0), EntryDate - 1) + 1 as week,
count(UserID) as balance
FROM table_points
where right(convert(varchar, EntryDate, 106), 8)='Mar 2015'
GROUP BY datediff(week, dateadd(week, datediff(week, 0, dateadd(month, datediff(month, 0, EntryDate), 0)), 0), EntryDate - 1) + 1
When i execute this query i get result for 4th and 5th week values but not for others week. Like
week balance
4 80
5 100
In first 3 weeeks there is no values in database so it will not showing the values . Can any one bring 0 value for 1st , 2nd and 3rd week.
I want result as
week balance
1 0
2 0
3 0
4 80
5 100
can any one help me out.
Create a calendar/date table, including all possible dates/weeks, and from there left join the table_points table.
If no data is present in table_points, it will return 0 for those weeks.
If no dates are present in a table, it's impossible to select these and get a zero.
Example:
SELECT d.week, count(t.UserID)
FROM dates d left join table_points t on
d.date = t.EntryDate
where right(convert(varchar, d.date, 106), 8)='Mar 2015'
group by d.week
A date table can be very useful for many applications and could be filled like:
date week year month WeekOfMonth DayOfWeek Day Month
2015-03-01 18 2015 3 1 5 Friday March
2015-03-02 18 2015 3 1 6 Friday March
2015-03-03 18 2015 3 1 7 Sunday March
2015-03-04 19 2015 3 2 1 Monday March
This works like PSVSupporter's solution but uses a CTE to get a date list instead of using another table:
;WITH Dates AS (
SELECT
[Date] = CONVERT(DATETIME,'03/01/2015')
UNION ALL SELECT
[Date] = DATEADD(DAY, 1, [Date])
FROM
Dates
WHERE
Date < '03/31/2015'
)
SELECT
datediff(week, dateadd(week, datediff(week, 0, dateadd(month, datediff(month, 0, ds.Date), 0)), 0), ds.Date - 1) + 1 as week,
count(tp.UserID) as balance
FROM Dates ds
left join table_points tp on tp.EntryDate = ds.Date
WHERE right(convert(varchar, ds.Date, 106), 8)='Mar 2015'
GROUP BY datediff(week, dateadd(week, datediff(week, 0, dateadd(month, datediff(month, 0, ds.Date), 0)), 0), ds.Date - 1) + 1
I took CTE from https://stackoverflow.com/a/7824893/3309109

Working days from a given date

User will select a date in frontend and flexibledays, say for example if they have selected '2014-07-17' as date and flexibledays as 2, then we need to display both 2 previous and next 2 working days as like below,
2014-07-15
2014-07-16
2014-07-17
2014-07-20
2014-07-21
excluding weekends (friday and saturday), for use weekends is friday and saturday.
I have used the below query
DECLARE #MinDate DATE, #MaxDate DATE;
SELECT #MinDate = DATEADD(Day, -#inyDays ,#dtDate), #MaxDate = DATEADD(Day,#inyDays ,#dtDate)
DECLARE #DayExclusionValue VARCHAR(20)
SELECT #DayExclusionValue = dbo.UDF_GetConfigSettingValue('DaysToExclude')
DECLARE #NumOfWeekends INT
SELECT #NumOfWeekends= (DATEDIFF(wk, #MinDate, #MaxDate) * 2) +(CASE WHEN DATENAME(dw, #MinDate) = 'Friday' THEN 1 ELSE 0 END) +(CASE WHEN DATENAME(dw, #MaxDate) = 'Saturday' THEN 1 ELSE 0 END)
SET #MaxDate = DATEADD(Day,#inyDays + #NumOfWeekends ,#dtDate)
;WITH CalculatedDates AS
(
SELECT dates = #MinDate
UNION ALL
SELECT DATEADD(day, 1, dates)
FROM CalculatedDates
WHERE DATEADD(day, 1, dates) <= #MaxDate
)
SELECT dates FROM CalculatedDates
WHERE dates >= CAST(GETDATE() AS DATE)
AND DATENAME(DW, dates) NOT IN (SELECT Value FROM UDF_GetTableFromString(#DayExclusionValue))
OPTION (MAXRECURSION 0);
but the above query is not working properly.
Can you pls suggest me any other solution.
This example will work for Oracle, you did not say what DB you were using. If you have a list of vacations you need to join that in as indicated. It would need to be a outerjoin, and you need to add a case or something so that the vacation tables 'exclude' days override the generated days.
Also I chose a multiplier on random. When only dealing with weekend 8 was more than enough, but if your vacation table includes a lot of consecutive vacation days it might no longer be.
select d from(
select rownum nn, d, sysdate - d, first_value (rownum) over (order by abs(sysdate-d)) zero_valu
from (
select sysdate+n d, to_char(sysdate+n,'DAY'), CASE to_char(sysdate+n,'D') WHEN '6' THEN 'exclude' WHEN '7' THEN 'exclude' ELSE 'include' END e_or_i from
(SELECT ROWNUM-9 n -- 9=flexibleday*8/2 +1
FROM ( SELECT 1 just_a_column
FROM dual
CONNECT BY LEVEL <= 16 -- 8=flexibleday * 8
)
)
) where e_or_i = 'include' -- in this step you need to join in a table of holidays or such if you need that.
) where abs(nn-7) <= 2 -- 2=flexiday
order by d
DECLARE #StartDate DATE = '2014-07-17';
SELECT *
FROM
(
--Show Closest Previous 2 Days Not In Friday or Saturday
SELECT TOP 2
DATEADD(DAY, -nr, #StartDate) CheckDate,
DATENAME(DW, DATEADD(DAY, -nr, #StartDate)) CheckName,
-nr CheckCount
FROM (VALUES(1),(2),(3),(4)) AS Numbers(nr)
WHERE DATENAME(DW, DATEADD(DAY, -nr, #StartDate)) NOT IN ('Friday','Saturday')
UNION ALL
--Show Todays Date If Not Friday or Saturday
SELECT TOP 1
DATEADD(DAY, +nr, #StartDate) CheckDate,
DATENAME(DW, DATEADD(DAY, +nr, #StartDate)) CheckName,
nr CheckCount
FROM (VALUES(0)) AS Numbers(nr)
WHERE DATENAME(DW, DATEADD(DAY, +nr, #StartDate)) NOT IN ('Friday','Saturday')
UNION ALL
--Show Closest Next 2 Days Not In Friday or Saturday
SELECT TOP 2
DATEADD(DAY, +nr, #StartDate) CheckDate,
DATENAME(DW, DATEADD(DAY, +nr, #StartDate)) CheckName,
nr CheckCount
FROM (VALUES(1),(2),(3),(4)) AS Numbers(nr)
WHERE DATENAME(DW, DATEADD(DAY, +nr, #StartDate)) NOT IN ('Friday','Saturday')
) d
ORDER BY d.CheckDate
I break it into 3 parts, previous 2 days, today (if applicable) and next 2 days
Here is the output:
CheckDate CheckName CheckCount
2014-07-15 Tuesday -2
2014-07-16 Wednesday -1
2014-07-17 Thursday 0
2014-07-20 Sunday 3
2014-07-21 Monday 4
I use the datename since not sure what ##datefirst your server is set to. the values() section is just a numbers table (you should create a numbers table as big as the amount of records you want to return plus any weekends you are crossing over) and then the TOP 2 in the first and last sections would be replaced with the number of days you wanted to return before and after.
**** Update with generic numbers table functionality added:
Here we declare the starting date and the number of previous and next days we would like to pull:
DECLARE #StartDate DATE = '2014-07-20';
DECLARE #MaxBusDays INT = 5
This next section creates a numbers table (can be easily found via google)
DECLARE #number_of_numbers INT = 100000;
;WITH
a AS (SELECT 1 AS i UNION ALL SELECT 1),
b AS (SELECT 1 AS i FROM a AS x, a AS y),
c AS (SELECT 1 AS i FROM b AS x, b AS y),
d AS (SELECT 1 AS i FROM c AS x, c AS y),
e AS (SELECT 1 AS i FROM d AS x, d AS y),
f AS (SELECT 1 AS i FROM e AS x, e AS y),
numbers AS
(
SELECT TOP(#number_of_numbers)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS number
FROM f
)
Now we use the numbers table and a row_number setting to pull only the number of rows before and after (plus the date of, if it's not fri/sat as wanted) that are working days (not fri/sat)
SELECT *
FROM
(
--Show Closest Previous x Working Days (Not Friday or Saturday)
SELECT * FROM
(
SELECT
DATEADD(DAY, -number, #StartDate) CheckDate,
DATENAME(DW, DATEADD(DAY, -number, #StartDate)) CheckName,
-number CheckCount,
ROW_NUMBER() OVER (ORDER BY number ASC) AS RowCounter
FROM Numbers
WHERE DATENAME(DW, DATEADD(DAY, -number, #StartDate)) NOT IN ('Friday','Saturday')
) a
WHERE a.RowCounter <= #MaxBusDays
UNION ALL
--Show Todays Date If Working Day (Not Friday or Saturday)
SELECT TOP 1
#StartDate CheckDate,
DATENAME(DW, #StartDate) CheckName,
0 CheckCount,
0 RowCounter
WHERE DATENAME(DW, #StartDate) NOT IN ('Friday','Saturday')
UNION ALL
--Show Closest Next x Working Days (Not Friday or Saturday)
SELECT * FROM
(
SELECT
DATEADD(DAY, +number, #StartDate) CheckDate,
DATENAME(DW, DATEADD(DAY, +number, #StartDate)) CheckName,
number CheckCount,
ROW_NUMBER() OVER (ORDER BY number ASC) AS RowCounter
FROM Numbers
WHERE DATENAME(DW, DATEADD(DAY, +number, #StartDate)) NOT IN ('Friday','Saturday')
) b
WHERE b.RowCounter <= #MaxBusDays
) c
ORDER BY c.CheckDate
Here is the output: (2014-07-20 is the middle row)
CheckDate CheckName CheckCount RowCounter
2014-07-13 Sunday -7 5
2014-07-14 Monday -6 4
2014-07-15 Tuesday -5 3
2014-07-16 Wednesday -4 2
2014-07-17 Thursday -3 1
2014-07-20 Sunday 0 0
2014-07-21 Monday 1 1
2014-07-22 Tuesday 2 2
2014-07-23 Wednesday 3 3
2014-07-24 Thursday 4 4
2014-07-27 Sunday 7 5