I want to find nth day of given month(eg:- first Sunday,second Tuesday,last friday etc) in sql server 2005.I had used
DATEADD(WEEK, DATEDIFF(WEEK, 0,
DATEADD(DAY, 6 - DATEPART(DAY, GETDATE()), GETDATE())), 6)
it will return first Sunday of a month but don't know how to find the nth day of month I need to return nth day as per the user's request
Can any please help to find this
Any help or suggestions would be grateful
Thanks And Regards
Many data warehouses have a date dimension. Things like dates and day of week are contained in this table. It can be indexed and has relatively good speed since 100 years x 365 days a year is not much data.
Below I use a tally table to create a common table expression for this year. I create another one for November 2014. From there, I use a windowing function to rank order the day of weeks by occurrence.
If you did have all the data in the date dimension, you could even place the occurrence number in the table for that month.
The example below pulls the third Friday of the month of November 2014.
--
-- Why not use a date dimension if you need this functionality often?
--
;
with
cte_Tally
as
(
SELECT ROW_NUMBER() over (order by (select 1)) as rn
FROM master.sys.all_columns c
),
cte_Year
as
(
select
rn as my_doy_num,
dateadd(d, rn, '20140101') as my_day_dte,
month(dateadd(d, rn, '20140101')) as my_month_num,
case month(dateadd(d, rn, '20140101'))
when 1 then 'jan'
when 2 then 'feb'
when 3 then 'mar'
when 4 then 'apr'
when 5 then 'may'
when 6 then 'jun'
when 7 then 'jul'
when 8 then 'aug'
when 9 then 'sep'
when 10 then 'oct'
when 11 then 'nov'
when 12 then 'dec'
else ''
end as my_month_desc,
datepart(dw, dateadd(d, rn, '20140101')) as my_dow_num,
case datepart(dw, dateadd(d, rn, '20140101'))
when 1 then 'sun'
when 2 then 'mon'
when 3 then 'tue'
when 4 then 'wed'
when 5 then 'thu'
when 6 then 'fri'
when 7 then 'sat'
else ''
end as my_dow_desc
from cte_Tally
where rn < 365
),
cte_Nov_2014
as
(
select
my_day_dte,
my_month_desc,
my_dow_desc,
ROW_NUMBER() over (partition by my_dow_desc order by my_day_dte, my_dow_desc) as my_occurence
from cte_Year
where my_month_num = 11
--order by my_dow_desc
)
select *
from cte_Nov_2014
where my_dow_desc = 'fri' and my_occurence = 3
-- 1 find the first nth weekday (Mon/Tue/Wed/Thu/Fri/Sat/Sun) of the month where #dt is in
-- for example, find the 5th Friday of the month of Aug, 2013
declare #nth int=5, #dt datetime='2013-08-12';
declare #dw tinyint =5 -- 1=Mon,2= Tue,3=Wed, 4=Thur, 5=Fri, 6=Sat, 7=Sun
select [1st_nth_wkday]=case when datepart(dw, dateadd(month,
datediff(month,0,#dt),0)+##datefirst-1) >= #dw
then dateadd(day, (#nth-1)*7 + (7-(datepart(dw, dateadd(month,
datediff(month,0,#dt),0)+##datefirst-1)-#dw)), dateadd(month,
datediff(month,0,#dt),0))
else dateadd(day, (#nth-1)*7 + (0-(datepart(dw, dateadd(month,
datediff(month,0,#dt),0)+##datefirst-1)-#dw)), dateadd(month,
datediff(month,0,#dt),0))
end
go
-- 2 find the last nth weekday (Mon/Tue/Wed/Thu/Fri/Sat/Sun) of the month
where #dt is in
-- find the 2nd last Sunday of current month
declare #nth int=2, #dt datetime=current_timestamp;
declare #dw tinyint =7 -- 1=Mon,2= Tue,3=Wed, 4=Thur, 5=Fri, 6=Sat, 7=Sun
select [last_nth_wkday]=case when datepart(dw, dateadd(month,
datediff(month,0,#dt)+1,0)-1+##datefirst-1) >= #dw
then dateadd(day, -(#nth-1)*7 - (datepart(dw, dateadd(month,
datediff(month,0,#dt)+1,0)-1+##datefirst-1)-#dw), dateadd(month,
datediff(month,0,#dt)+1,0)-1)
else dateadd(day, -(#nth)*7 - (datepart(dw, dateadd(month,
datediff(month,0,#dt)+1,0)-1+##datefirst-1)-#dw), dateadd(monI th,
datediff(month,0,#dt)+1,0)-1)
end
go
I actually blogged this here: http://www.sqlservercentral.com/blogs/jeffrey_yao/2014/03/02/fun-in-datetime-calculations/
Related
Is there any way to calculate length of service of an Employee when there is a gap in his tenure. I have a period1 from '08/09/2003' to '06/25/2009', period 2 is from '06/10/2015' to '03/31/2022' or GETDATE(). can someone explain me how can I calculate the whole period(Length of service) when there is a gap like this for that Employee ?
All dates above are in 'MM/DD/YYYY' format.
So, the Period1 service is - 5 Years, 10 Months, 16 Days
Period2 service is - 6 Years, 9 Months, 21 Days
The requirements is
total service should be - 12 Years, 8 Months, 7 Days
I know to calculate one period length but having issues while I try to calculate length when there is a gap
This soluction works pretty good. Although, counting days this way is kind off weird. If you start work at June 22nd and end July 22nd, have you then worked 1 month? or 30 days?
DECLARE #ServiceTbl TABLE (StartDate Date, EndDate Date)
DECLARE #StartDate date, #EndDate date
DECLARE #Sum TABLE (Years int, months int, days int)
INSERT INTO #ServiceTbl
SELECT '08/09/2003', '06/25/2009'
UNION SELECT '06/10/2015', null
DECLARE cur CURSOR LOCAL STATIC FORWARD_ONLY FOR
SELECT StartDate, ISNULL(EndDate, GetDate())
FROM #ServiceTbl
OPEN cur
WHILE 1=1
BEGIN
FETCH NEXT FROM cur INTO #StartDate, #EndDate
if ##FETCH_STATUS<>0 break
;WITH CTE AS(
SELECT
0 as countYear
, 0 as countMonth
, DATEDIFF(Day, #StartDate, EOMONTH(#StartDate, 0)) as CountDays
, EOMONTH(#StartDate, 1) as nextDate
, #EndDate as EndDate
, 0 as id
UNION ALL SELECT
CASE
WHEN nextDate < EndDate
AND countMonth + 1 = 12
THEN countYear + 1
ELSE countYear END
, CASE
WHEN nextDate < EndDate
AND countMonth + 1 < 12
THEN countMonth + 1
WHEN nextDate < EndDate
THEN 0
ELSE countMonth END
, CountDays + CASE
WHEN EndDate < EOMONTH(nextDate, 1)
THEN DATEDIFF(day, nextDate, EndDate)
ELSE 0 END
, EOMONTH(nextDate, 1)
, EndDate
, id + 1
FROM CTE
WHERE nextDate < EndDate
)
INSERT INTO #Sum
SELECT TOP 1 countYear, countMonth, CountDays
FROM CTE
ORDER BY id Desc
END
CLOSE cur DEALLOCATE cur
SELECT SUM(Years) + FLOOR((SUM(months) + FLOOR(SUM(Days) / 31)) / 12) as Years
, (SUM(months) + FLOOR(SUM(Days) / 31)) % 11 as Months
, SUM(days) % 30 as Days
FROM #Sum
You cannot calculate the years, months and days for each period and then sum them, because you don't know how to sum the days (31, 30, 29 or 28)
So, maybe if we do sum up all these values, and then add them to the enddate of the first period then we can calculate the values from the startdate and this extended endate ?
The outcome will never be exact, because of the difference in months length, but this maybe is as close that you can get
I tested it in this DBFiddle
But, I don't get your expected result because I still don't see how you get 9 months and 21 days from your second period.
Take a look at the dBFiddle, if it is not what you are looking for then maybe it helps inspire you in the right direction
select t2.period,
t2.startdate,
t2.enddate,
datediff(month, t2.startdate, t2.enddate) / 12 as years,
datediff(month, t2.startdate, t2.enddate) - ((datediff(month, t2.startdate, t2.enddate) / 12) * 12) as months,
datediff(day,
datefromparts(2022, datepart(month, t2.enddate), datepart(day, t2.startdate)),
datefromparts(2022, datepart(month, t2.enddate), datepart(day, t2.enddate))
) as days
from (select temp.period,
min(temp.startdate) as startdate,
dateadd(day, sum(temp.days), dateadd(month, sum(temp.months), dateadd(year, sum(temp.years), min(temp.startdate)))) as enddate
from ( select t.period,
t.startdate,
t.enddate,
datediff(month, t.startdate, t.enddate) / 12 as years,
datediff(month, t.startdate, t.enddate) - ((datediff(month, t.startdate, t.enddate) / 12) * 12) as months,
datediff(day,
datefromparts(2022, datepart(month, t.enddate), datepart(day, t.startdate)),
datefromparts(2022, datepart(month, t.enddate), datepart(day, t.enddate))
) as days
from table1 t
) temp
group by temp.period
) t2
How to get max Saturday dates in a column of each month in SQL Server. Can someone please help me.
Now I need only the dates which has last Saturday of month.
For example,
The table has
07-08-2021 - Saturday
14-08-2021 - Saturday
21-08-2021 - Saturday
28-08-2021 - Saturday
04-09-2021 - Saturday
11-09-2021 - Saturday
18-09-2021 - Saturday
25-09-2021 - Saturday
Suppose we are in August month, I need to select last Saturday of that month( ONLY 28-08-2021)
Suppose we are in September month, I need to select last Saturday of that month( ONLY 25-09-2021)
Output:
28-08-2021
25-09-2021
assuming you have a datefield in your table (I will refer to it here as such in the below query)
with week as (
select
date_trunc('week', datefield + interval '2 day') - interval '2 day' as week_date
-- ^this adjusts the week date_trunc to start on Saturday (it starts on Monday by default)
from sometable
)
select
extract(month from week_date) as month_num,
max(week_date) as last_saturday
from week
group by month_num
note: if you only have partial data for the current month, this query will need to be altered slightly, but you didn't give me a lot to go off of here
A CTE is defined to populate the day name of entire month and then the requirement is filtered.
;with GetDates As (
select CAST('01-01-2021' as date) as StartDate, datename(dw, '09-01-2021') as Day_Name
UNION ALL
select DATEADD(day,1, StartDate), datename(dw, DATEADD(day,1, StartDate))
from GetDates
where StartDate < '09-30-2021'
)
select max(StartDate)
from GetDates where Day_Name = 'Saturday'
group by month(StartDate)
OPTION (MAXRECURSION 500)
Here is a function to calculate the Nth Weekday - which can calculate from the beginning of the month or from the end of the month.
Alter Function dbo.fnGetNthWeekDay (
#theDate datetime
, #theWeekday int
, #theNthDay int
)
Returns Table
As
Return
/*
Adapted from a version published by Peter Larrson - with minor modifications for performance
and restructured to eliminate usage of a derived table.
Inputs:
#theDate any date in the month
#theWeekday the weekday to calculate: 1 = Monday
2 = Tuesday
3 = Wednesday
4 = Thursday
5 = Friday
6 = Saturday
7 = Sunday
#theNthDay the week count where positive is from beginning of the month
and negative is from end of month
Outputs:
#theDate the date entered
#theNthDate the Nth date of the month
*/
Select theDate = #theDate
, dt.nthDate
From (Values (dateadd(month, datediff(month, #theNthDay, #theDate), 0))) As mm(FirstOfMonth)
Cross Apply (Values (dateadd(day, 7 * #theNthDay - 7 * sign(#theNthDay + 1)
+ (#theWeekday + 6 - datediff(day, -53690, mm.FirstOfMonth) % 7) % 7, mm.FirstOfMonth))) As dt(nthDate)
Where #theWeekday Between 1 And 7
And datediff(month, dt.nthDate, #theDate) = 0
And #theNthDay In (-5, -4, -3, -2, -1, 1, 2, 3, 4, 5);
Go
You can then call it like this:
Select * From dbo.fnGetNthWeekDay('2021-08-15', 6, -1) nwd
First Day of financial year is April 1st.
T-SQL Query to return April 1st for the getdate()
Financial Year: April 1st to March 31st
Try this:
select DATEFROMPARTS(Yr, 4, 1) [start], DATEFROMPARTS(Yr + 1, 3, 31) [end] from
(select case when DATEPART(month, getdate()) < 4 then DATEPART(year, getdate()) - 1 else DATEPART(year, getdate()) end Yr) a
declare #today date = '2018-06-21'
select fin_year = dateadd(month, 3,
dateadd(year,
datepart(year,
dateadd(month, -3, #today)) - 1900, 0))
the expression datepart(year, dateadd(month, -3, #today)) is to get the current financial year. Since your financial year is Apr 1, for Jan 1 to Mar 31, subtracting 3 months from it, will give you the correct year (fiscal year = financial year).
After that it is just to form the date Apr 1 with that year
DECLARE #DateToUse DATETIME = GETDATE(),
#FinancialYearStart DATETIME
DECLARE #DayPart INT = DATEPART(DAY, #DateToUse),
#MonthPart INT = DATEPART(MONTH, #DateToUse),
#YearPart INT = DATEPART(YEAR, #DateToUse),
#StartMonth INT = 4, -- April
#StartDay INT = 1 -- 1st
SELECT DATETIMEFROMPARTS((
#YearPart - CASE
WHEN #MonthPart > #StartMonth
OR (
#MonthPart = #StartMonth
AND #DayPart >= #StartDay
)
THEN 0
ELSE 1
END
), #StartMonth, #StartDay, 0, 0, 0, 0)
I just knocked this up and it produces 2020-04-01 00:00:00.000 right now. I threw some other dates at it and it seemed to work nicely. Obviously the month/day can easily be changed by changing the variables at the top.
It's not the shortest block of SQL, but it's easy to use for different dates. A lot of other answers I've seen across different questions have returned different formats like a string or just the year part. Whereas this datetime will allow it to be used for datetime comparisons.
I tried but could not get the right solution. I want an SQL query that lists all the weekend dates of the current year.
I tried this SQL query:
WITH hier(num, lvl) AS (
SELECT 0, 1
UNION ALL
SELECT 100, 1
UNION ALL
SELECT num + 1, lvl + 1
FROM hier
WHERE lvl < 100
)
SELECT lvl [Week],
convert(date,DATEADD(dw, -DATEPART(dw, DATEADD(wk,DATEDIFF(wk,0,'12/31/'+convert(nvarchar,YEAR(getdate()))), 0)+6 ),
DATEADD(wk, DATEDIFF(wk,0,'12/31/'+convert(nvarchar,YEAR(getdate()))), 0)+6 ) - num * 7,101) [End Date]
FROM hier a
where num < 52
ORDER BY [End Date] asc
Its output is like this:
Week End date
52 2012-01-14
51 2012-01-21
50 2012-01-28
49 2012-02-04
I want the dates to start from the beginning – so, the above is missing one weekend, which is 2012-07-01. Also, I want the week numbers to show as 1, 2, 3... instead of 52, 51....
Check out this blog post.
Your question is explained in detail.
DECLARE #Year AS INT,
#FirstDateOfYear DATETIME,
#LastDateOfYear DATETIME
-- You can change #year to any year you desire
SELECT #year = 2010
SELECT #FirstDateOfYear = DATEADD(yyyy, #Year - 1900, 0)
SELECT #LastDateOfYear = DATEADD(yyyy, #Year - 1900 + 1, 0)
-- Creating Query to Prepare Year Data
;WITH cte AS (
SELECT 1 AS DayID,
#FirstDateOfYear AS FromDate,
DATENAME(dw, #FirstDateOfYear) AS Dayname
UNION ALL
SELECT cte.DayID + 1 AS DayID,
DATEADD(d, 1 ,cte.FromDate),
DATENAME(dw, DATEADD(d, 1 ,cte.FromDate)) AS Dayname
FROM cte
WHERE DATEADD(d,1,cte.FromDate) < #LastDateOfYear
)
SELECT FromDate AS Date, Dayname
FROM CTE
WHERE DayName IN ('Saturday','Sunday') -- For Weekend
/*
WHERE DayName LIKE 'Sunday'
WHERE DayName NOT IN ('Saturday','Sunday') -- For Weekday
WHERE DayName LIKE 'Monday' -- For Monday
WHERE DayName LIKE 'Sunday' -- For Sunday
*/
OPTION (MaxRecursion 370)
Will this help
DECLARE #startDate DATETIME, #endDate DATETIME
SELECT #startDate = '2012-01-01', #endDate = '2012-12-31'
;WITH Calender AS (
SELECT #startDate AS dt
UNION ALL
SELECT dt + 1 FROM Calender
WHERE dt + 1 <= #endDate
)
SELECT
dt
,NameMonth = DATENAME(Month, dt)
,NameDay = DATENAME (Weekday,dt)
,WeekofYr = DATEPART(WEEK, dt) FROM Calender
WHERE DATENAME (Weekday,dt) IN ('Sunday')
Option(MaxRecursion 0)
Result(Partial)
dt NameMonth NameDay WeekofYr
2012-01-01 00:00:00.000 January Sunday 1
2012-01-08 00:00:00.000 January Sunday 2
...............................................
...............................................
2012-12-30 00:00:00.000 December Sunday 53
you can try this
DECLARE #FirstDateOfYear DATETIME
SET #FirstDateOfYear = ’2010-01-01′
SELECT DISTINCT DATEADD(d, number, #FirstDateOfYear),
CASE DATEPART(dw, DATEADD(d, number, #FirstDateOfYear))
WHEN 7 THEN ‘Saturday’
WHEN 1 THEN ‘Sunday’
ELSE ‘Work Day’
END
FROM master..spt_values
WHERE number BETWEEN 0 AND 364
AND (DATEPART(dw, DATEADD(d, number, #FirstDateOfYear)) = 1 OR DATEPART(dw, DATEADD(d, number, #FirstDateOfYear)) = 7)
ORDER BY DATEADD(d, number, #FirstDateOfYear)
Try to find the first Saturday by doing this:
Start on 2012-01-01
If it's not a Saturday, add a day
Goto 2
Then, into a temporary table, add that date and the following date (Sunday).
After that, loop the following:
Add 7 and 8 days to the last Saturday you found (you get the following Saturday and Sunday)
Check whether they are still in 2012
If they are, store them in temp table and goto 1
There may be more elegant ways, but that's my quick & dirty solution. As you didn't post any code of what you've tried, I'll leave the implementation up to you.
this also works
declare #dat datetime, #add int
set #dat = '20120101'
set #add = datepart(w,#dat)
set #add = 5 - #add -- friday
set #dat = dateadd(d,#add,#dat)
while #dat <= '20121231'
begin
print #dat
set #dat = dateadd(d,7,#dat)
end
;with AllDaysOfYear (Day) as (
select DATEADD(year,DATEDIFF(year,0,CURRENT_TIMESTAMP),0) --Jan 1st
union all
select DATEADD(day,1,Day) from AllDaysOfYear
where DATEPART(year,DATEADD(day,1,Day)) = DATEPART(year,CURRENT_TIMESTAMP)
)
select
ROW_NUMBER() OVER (ORDER BY Day) as WeekNo,
Day
from
AllDaysOfYear
where
DATEPART(weekday,Day) = DATEPART(weekday,'20120714')
option (maxrecursion 0)
First, generate a set of all of the days in the current year (AllDaysInYear). Then, select those whose weekday is a saturday. The value I've used ('20120714') isn't terribly important - it just has to be any saturday, from any year. I'm just using it to avoid needing to have particular DATEFIRST or language settings.
This query shows how to get the first day of this year and the first day of the next year in the first part. The first day of the next year is calculated once so as not to keep getting and comparing the year parts.
;WITH cte(TheDate,NextYear) AS
(
SELECT CAST(CONVERT(CHAR(4),GETDATE(),112)+'0101' AS DATETIME),
CAST(YEAR(GETDATE())*10000+10101 AS CHAR(8))
UNION ALL
SELECT DateAdd(d,1,TheDate),NextYear
FROM cte
WHERE DateAdd(d,1,TheDate)<NextYear
)
SELECT Week = DatePart(wk,TheDate),
TheDate
FROM cte
WHERE DateName(dw,TheDate) in ('Saturday')
ORDER BY TheDate
OPTION (MAXRECURSION 366)
with t as
(
select 1 b
union all
select 1 b
union all
select 1 b
union all
select 1 b
union all
select 1 b
union all
select 1 b
union all
select 1 b
union all
select 1 b
)
select * from
(
select
current_timestamp
-datepart(dy,current_timestamp)
+row_number() over (order by t.b) d
from t, t t1, t t2
) tmp
where datepart(yyyy,d)=datepart(yyyy,current_timestamp)
and
DATENAME(dw,d)='sunday'
DECLARE #Year AS INT
SELECT #Year = 2020
;WITH weekends AS (
SELECT DATEFROMPARTS(#Year, 1, 1) AS dt
UNION ALL
SELECT DATEADD(DAY, 1, dt)
FROM weekends
WHERE dt < DATEFROMPARTS(#Year, 12, 31)
)
SELECT dt, DATENAME(MONTH, dt), DATENAME(DW, dt)
FROM weekends
WHERE DATEPART(DW, dt) IN (1, 7)
OPTION(MaxRecursion 366)
So, I'd like to, for a Start Date and End Date, determine how many particular days of the week occur between these two dates.
So how many mondays, tuesdays, etc
I know I can do this with a loop between the Start and End Date and check each day, but the difference could possibly be a great number of days. I'd prefer something that didn't require a loop. Any ideas? (Must be supported in SQL Server 2005+)
Given what I think you're trying to get, this should do it:
SET DATEFIRST 1
DECLARE
#start_date DATETIME,
#end_date DATETIME
SET #start_date = '2011-07-11'
SET #end_date = '2011-07-22'
;WITH Days_Of_The_Week AS (
SELECT 1 AS day_number, 'Monday' AS day_name UNION ALL
SELECT 2 AS day_number, 'Tuesday' AS day_name UNION ALL
SELECT 3 AS day_number, 'Wednesday' AS day_name UNION ALL
SELECT 4 AS day_number, 'Thursday' AS day_name UNION ALL
SELECT 5 AS day_number, 'Friday' AS day_name UNION ALL
SELECT 6 AS day_number, 'Saturday' AS day_name UNION ALL
SELECT 7 AS day_number, 'Sunday' AS day_name
)
SELECT
day_name,
1 + DATEDIFF(wk, #start_date, #end_date) -
CASE WHEN DATEPART(weekday, #start_date) > day_number THEN 1 ELSE 0 END -
CASE WHEN DATEPART(weekday, #end_date) < day_number THEN 1 ELSE 0 END
FROM
Days_Of_The_Week
I'm not sure what the OP is after, this will give a count per weeks day:
SET DATEFIRST 1
DECLARE #StartDate datetime
,#EndDate datetime
SELECT #StartDate='7/13/2011'
,#EndDate='7/28/2011'
;with AllDates AS
(
SELECT #StartDate AS DateOf, datename(weekday,#StartDate) AS WeekDayName, datepart(weekday,#StartDate) AS WeekDayNumber
UNION ALL
SELECT DateOf+1, datename(weekday,DateOf+1), datepart(weekday,DateOf+1)
FROM AllDates
WHERE DateOf<#EndDate
)
SELECT COUNT(*) CountOf,WeekDayName FROM AllDates GROUP BY WeekDayName,WeekDayNumber ORDER BY WeekDayNumber
OUTPUT:
CountOf WeekDayName
----------- ------------------------------
2 Monday
2 Tuesday
3 Wednesday
3 Thursday
2 Friday
2 Saturday
2 Sunday
(7 row(s) affected)
this will give a count of Monday to Friday days:
SET DATEFIRST 1
DECLARE #StartDate datetime
,#EndDate datetime
SELECT #StartDate='7/13/2011'
,#EndDate='7/28/2011'
;with AllDates AS
(
SELECT #StartDate AS DateOf, datepart(weekday,#StartDate) AS WeekDayNumber
UNION ALL
SELECT DateOf+1, datepart(weekday,DateOf+1)
FROM AllDates
WHERE DateOf<#EndDate
)
SELECT COUNT(*) AS WeekDayCount FROM AllDates WHERE WeekDayNumber<=5
OUTPUT:
WeekDayCount
------------
12
(1 row(s) affected)
If you have a holiday table, you can join it in and remove those as well. Here is a slightly different version that may preform better:
SET DATEFIRST 1
DECLARE #StartDate datetime
,#EndDate datetime
SELECT #StartDate='7/13/2011'
,#EndDate='7/28/2011'
;with AllDates AS
(
SELECT #StartDate AS DateOf, datepart(weekday,getdate()) AS WeekDayNumber
UNION ALL
SELECT DateOf+1, (WeekDayNumber+1) % 7
FROM AllDates
WHERE DateOf<#EndDate
)
SELECT COUNT(*) AS WeekDayCount FROM AllDates WHERE WeekDayNumber>0 AND WeekDayNumber<6
--I don't like using "BETWEEN", ">", ">=", "<", and "<=" are more explicit in defining end points
produces same output as original query.
This assume standard settings but cans be adapted
DECLARE #StartDate datetime, #EndDate datetime
SELECT #StartDate='20110601', #EndDate='20110630'
;WITH AllDates AS
(
SELECT #StartDate AS DateOf, datepart(weekday, #StartDate) AS WeekDayNumber
UNION ALL
SELECT DateOf+1, datepart(weekday, DateOf+1)
FROM AllDates
WHERE DateOf < #EndDate
)
SELECT SUM(CASE WHEN WeekDayNumber BETWEEN 2 AND 6 THEN 1 ELSE 0 END) AS WeekDayCount
FROM AllDates
OPTION (MAXRECURSION 0)
This should be valid for SQL Server, and should be internationlization safe (note: I do not have a server to test this against).
SELECT datediff(day, #start, #end) - datediff(week, #start, #end) * 2
- CASE WHEN datepart(weekday, #start)
IN (datepart(weekday, '1970-01-03'),
datepart(weekday, '1970-01-04'))
THEN 1
ELSE 0 END,
- CASE WHEN datepart(weekday, #end)
IN (datepart(weekday, '1970-01-03'),
datepart(weekday, '1970-01-04'))
THEN 1
ELSE 0 END
Give that a whirl.
Given the clarification, this should get the number of each of the days.
Uses no recursion, and should be completely international-safe. You will have to adjust start/end date parameters for inclusion/exclusion as necessary (The DB2 version I was using to check this excluded the start date, but included the end date, for example).
WITH dayOfWeek (name, dayNumber) as (VALUES(dayname(weekday, '1970-01-01'), daypart(weekday, '1970-01-01')),
(dayname(weekday, '1970-01-02'), daypart(weekday, '1970-01-02')),
(dayname(weekday, '1970-01-03'), daypart(weekday, '1970-01-03')),
(dayname(weekday, '1970-01-04'), daypart(weekday, '1970-01-04')),
(dayname(weekday, '1970-01-05'), daypart(weekday, '1970-01-05')),
(dayname(weekday, '1970-01-06'), daypart(weekday, '1970-01-06')),
(dayname(weekday, '1970-01-07'), daypart(weekday, '1970-01-07')))
SELECT name, dayNumber, datediff(weeks, #start, #end)
+ CASE WHEN datepart(weekday, #end) >= dayNumber THEN 1 ELSE 0 END
- CASE WHEN datepart(weekday, #start) >= dayNumber THEN 1 ELSE 0 END
FROM dayOfWeek
Does that help any?
you can use DATEDIFF and DATEPART functions + some basic math to get the desired result without looping.
I would have simply added as a comment to the marked answer, but do not have enough "reputation". Instead of hardcoding the day_number which is dependent on datefirst, you could set it by discovering the weekday for each day of the week:
;WITH Days_Of_The_Week AS (
SELECT DATEPART(dw, '2007-01-01') AS day_number, 'Monday' AS day_name UNION ALL -- 2007-01-01 is a known Monday
SELECT DATEPART(dw, '2007-01-02') AS day_number, 'Tuesday' AS day_name UNION ALL
SELECT DATEPART(dw, '2007-01-03') AS day_number, 'Wednesday' AS day_name UNION ALL
SELECT DATEPART(dw, '2007-01-04') AS day_number, 'Thursday' AS day_name UNION ALL
SELECT DATEPART(dw, '2007-01-05') AS day_number, 'Friday' AS day_name UNION ALL
SELECT DATEPART(dw, '2007-01-06') AS day_number, 'Saturday' AS day_name UNION ALL
SELECT DATEPART(dw, '2007-01-07') AS day_number, 'Sunday' AS day_name
)
#start_date date = '2017-08-11',
#end_date date = '2017-08-27',
#weekday int = 7,
#count int output
As
Begin
Declare #i int = 0
set #count = 0
while(#i <= (select Datediff(Day, #start_date, #end_date)))
begin
if(Dateadd(Day, #i, #start_date) > #end_date)
break
if(Datepart(weekday, Dateadd(Day, #i, #start_date)) = #weekday)
set #count += 1
set #i += 1
end
select #count