Related
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
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/
I would like to build an SQL query which calculates the difference between 2 dates, without counting the week-end days in the result.
Is there any way to format the dates to obtain this result ? For example for Oracle database :
select sysdate - creation_dttm from the_table
You should try with a function :
CREATE FUNCTION TOTAL_WEEKDAYS(date1 DATE, date2 DATE)
RETURNS INT
RETURN ABS(DATEDIFF(date2, date1)) + 1
- ABS(DATEDIFF(ADDDATE(date2, INTERVAL 1 - DAYOFWEEK(date2) DAY),
ADDDATE(date1, INTERVAL 1 - DAYOFWEEK(date1) DAY))) / 7 * 2
- (DAYOFWEEK(IF(date1 < date2, date1, date2)) = 1)
- (DAYOFWEEK(IF(date1 > date2, date1, date2)) = 7);
Test :
SELECT TOTAL_WEEKDAYS('2013-08-03', '2013-08-21') weekdays1,
TOTAL_WEEKDAYS('2013-08-21', '2013-08-03') weekdays2;
Result :
| WEEKDAYS1 | WEEKDAYS2 |
-------------------------
| 13 | 13 |
I have found another way to do calculate the difference, by using only SQL :
select sysdate - creation_dttm
- 2 * (to_char(sysdate, 'WW') - to_char(creation_dttm, 'WW'))
from the_table
I have found several of the answers on this thread to not do what they claim. After some experimentation, testing and adjusting, I have this to contribute.
declare #firstdate Date
declare #seconddate Date
set #firstDate = convert(date, '2016-03-07')
set #seconddate = convert(date, '2016-04-04')
select (datediff(dd, #firstDate, #secondDate)) -
(( DateDiff(wk, #firstDate, #secondDate) * 2) -
case when datepart(dw, #FirstDate) = 7 then 1 else 0 end -
case when datepart(dw, #secondDate) = 7 then -1 else 0 end)
Test harness included - you can just adjust the two dates and run your own tests. This assumes that the difference between two adjacent weekday dates is 1. If your country uses different days to signify weekend, then you will have to set the date-base accordingly so your "Saturday" is 7, and your "sunday" is 1.
From a previous post:
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '2008/10/01'
SET #EndDate = '2008/10/31'
SELECT
(DATEDIFF(dd, #StartDate, #EndDate) + 1)
-(DATEDIFF(wk, #StartDate, #EndDate) * 2)
-(CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, #EndDate) = 'Saturday' THEN 1 ELSE 0 END)
Here is an example:
There are four variables, the first two are self-explanatory, just enter the date in YYYYMMDD format, the 3rd one is to set the number of normal work days in a given week, so if a site works 6 days a week, set it to 6, five days a week enter 5, etc. Finally, the
DATE_SEQ_NORML_FACTOR should be 1 when running against Oracle. This is to line up the Julian date to equal to 1 on Monday, 2 on Tuesday, etc when applying the MOD 7. Other DB will probably have different values between 0 and 6, so test it out before you use against other DBs.
Here are the limitations:
1. This formula assumes the first day of the week is MONDAY.
2. This formula assumes all days within the same week are CONTINUOUS.
3. This formula will work ONLY when the two dates involved in the calculation falls on a week day or work day, eg. the "Start Date" on a SATURDAY when the location works only MON-FRI will not work.
SELECT
&&START_DATE_YYYYMMDD "Start Date", --in YYYYMMDD format
&&END_DATE_YYYYMMDD "End Date", --in YYYYMMDD format
&&WK_WORK_DAY_CNT "Week Work Day Count", --Number of work day per week
&&DATE_SEQ_NORML_FACTOR "Normalization Factor", --set to 1 when run in Oracle
CASE
WHEN
FLOOR( TO_CHAR( TO_DATE( &&START_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) + &&DATE_SEQ_NORML_FACTOR / 7 ) =
FLOOR( TO_CHAR( TO_DATE( &&END_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) + &&DATE_SEQ_NORML_FACTOR / 7 )
THEN(
TO_CHAR( TO_DATE( &&END_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) -
TO_CHAR( TO_DATE( &&START_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) + 1
)
ELSE(
(
&&WK_WORK_DAY_CNT - MOD( TO_CHAR( TO_DATE( &&START_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) + &&DATE_SEQ_NORML_FACTOR , 7 ) + 1
) +
MOD( TO_CHAR( TO_DATE( &&END_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) + &&DATE_SEQ_NORML_FACTOR , 7 ) +
(
(
(
TO_CHAR( TO_DATE( &&END_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) -
MOD( TO_CHAR( TO_DATE( &&END_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) + &&DATE_SEQ_NORML_FACTOR , 7 )
) -
(
TO_CHAR( TO_DATE( &&START_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) +
(
7 -
(
MOD( TO_CHAR( TO_DATE( &&START_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) + &&DATE_SEQ_NORML_FACTOR , 7 )
)
)
)
) / 7 * &&WK_WORK_DAY_CNT
)
) END "Week Day Count"
FROM DUAL
We've incorporated the following logic into several reports with great success. Sorry, I no longer recall the source of this script to give them credit.
DECLARE #range INT;
SET #range = DATEDIFF(DAY, #FirstDate, #SecondDate);
SET #NumWorkDays = (
SELECT
#range / 7 * 5 + #range % 7 - (
SELECT COUNT(*)
FROM (
SELECT 1 AS d
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
UNION ALL SELECT 6
UNION ALL SELECT 7
) weekdays
WHERE d <= #range % 7
AND DATENAME(WEEKDAY, #SecondDate - d) IN ('Saturday', 'Sunday'))
);
I've updated #JOpuckman's function to take into account that different regions don't always have a weekend of Saturday and Sunday. Here's the code in case anyone else needs to apply this globally;
DECLARE #FirstDate DateTime
DECLARE #SecondDate DateTime
SET #FirstDate = '08-20-2012'
SET #SecondDate = '08-24-2012'
DECLARE #range INT;
DECLARE #WeekendDayNameStart VARCHAR(50)
DECLARE #WeekendDayNameEnd VARCHAR(50)
SET #WeekendDayNameStart = 'FRIDAY'
SET #WeekendDayNameEnd = (
SELECT CASE #WeekendDayNameStart
WHEN 'SUNDAY' THEN 'MONDAY'
WHEN 'MONDAY' THEN 'TUESDAY'
WHEN 'TUESDAY' THEN 'WEDNESDAY'
WHEN 'WEDNESDAY' THEN 'THURSDAY'
WHEN 'THURSDAY' THEN 'FRIDAY'
WHEN 'FRIDAY' THEN 'SATURDAY'
WHEN 'SATURDAY' THEN 'SUNDAY'
END
)
DECLARE #NumWorkDays INT
SET #range = DATEDIFF(DAY, #FirstDate, #SecondDate);
SET #NumWorkDays = (
SELECT
#range / 7 * 5 + #range % 7 - (
SELECT COUNT(*)
FROM (
SELECT 1 AS d
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
UNION ALL SELECT 6
UNION ALL SELECT 7
) weekdays
WHERE d <= #range % 7
AND DATENAME(WEEKDAY, #SecondDate - d) IN (#WeekendDayNameStart, #WeekendDayNameEnd))
);
-- Calculate whether the current date is a working day
DECLARE #CurDateExtra INT
SET #CurDateExtra =
(
CASE DATENAME(WEEKDAY, #SecondDate)
WHEN #WeekendDayNameStart THEN 0
WHEN #WeekendDayNameEnd THEN 0
ELSE 1
END
)
SET #NumWorkDays = #NumWorkDays + #CurDateExtra
SELECT #NumWorkDays
Calculates the difference in days between the two dates
Calculates the difference in week numbers and year numbers, subtracts the week numbers and then multiplies the result by 2 to calculate number of non-workdays between the two dates. If year numbers are different calculation of 52 * year difference + week number.
((sysdate - ced.created_dt) + ((((to_char(ced.created_dt,'IW') - ((to_char(sysdate,'YY') - to_char(ced.created_dt,'YY'))* 52))
- to_char(to_char(sysdate,'IW')))) * 2)) duration_in_weekdays
You can try this:
SELECT
Id,
DATEDIFF(d, datefrom, dateto) AS TotDays,
DATEDIFF(wk, datefrom, dateto) AS Wkds,
DATEDIFF(d, datefrom, dateto) - DATEDIFF(wk, datefrom, dateto) AS Days
FROM
YOURTABLE
If your company has a DATES table listing the date and relative work day, as most companies do, you could build a temp table that excludes duplicate relative work days that will also give you the ability to exclude holidays from your calculations as well as weekends.
I would like to build an SQL query which calculates the difference between 2 dates, without counting the week-end days in the result.
Is there any way to format the dates to obtain this result ? For example for Oracle database :
select sysdate - creation_dttm from the_table
You should try with a function :
CREATE FUNCTION TOTAL_WEEKDAYS(date1 DATE, date2 DATE)
RETURNS INT
RETURN ABS(DATEDIFF(date2, date1)) + 1
- ABS(DATEDIFF(ADDDATE(date2, INTERVAL 1 - DAYOFWEEK(date2) DAY),
ADDDATE(date1, INTERVAL 1 - DAYOFWEEK(date1) DAY))) / 7 * 2
- (DAYOFWEEK(IF(date1 < date2, date1, date2)) = 1)
- (DAYOFWEEK(IF(date1 > date2, date1, date2)) = 7);
Test :
SELECT TOTAL_WEEKDAYS('2013-08-03', '2013-08-21') weekdays1,
TOTAL_WEEKDAYS('2013-08-21', '2013-08-03') weekdays2;
Result :
| WEEKDAYS1 | WEEKDAYS2 |
-------------------------
| 13 | 13 |
I have found another way to do calculate the difference, by using only SQL :
select sysdate - creation_dttm
- 2 * (to_char(sysdate, 'WW') - to_char(creation_dttm, 'WW'))
from the_table
I have found several of the answers on this thread to not do what they claim. After some experimentation, testing and adjusting, I have this to contribute.
declare #firstdate Date
declare #seconddate Date
set #firstDate = convert(date, '2016-03-07')
set #seconddate = convert(date, '2016-04-04')
select (datediff(dd, #firstDate, #secondDate)) -
(( DateDiff(wk, #firstDate, #secondDate) * 2) -
case when datepart(dw, #FirstDate) = 7 then 1 else 0 end -
case when datepart(dw, #secondDate) = 7 then -1 else 0 end)
Test harness included - you can just adjust the two dates and run your own tests. This assumes that the difference between two adjacent weekday dates is 1. If your country uses different days to signify weekend, then you will have to set the date-base accordingly so your "Saturday" is 7, and your "sunday" is 1.
From a previous post:
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '2008/10/01'
SET #EndDate = '2008/10/31'
SELECT
(DATEDIFF(dd, #StartDate, #EndDate) + 1)
-(DATEDIFF(wk, #StartDate, #EndDate) * 2)
-(CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, #EndDate) = 'Saturday' THEN 1 ELSE 0 END)
Here is an example:
There are four variables, the first two are self-explanatory, just enter the date in YYYYMMDD format, the 3rd one is to set the number of normal work days in a given week, so if a site works 6 days a week, set it to 6, five days a week enter 5, etc. Finally, the
DATE_SEQ_NORML_FACTOR should be 1 when running against Oracle. This is to line up the Julian date to equal to 1 on Monday, 2 on Tuesday, etc when applying the MOD 7. Other DB will probably have different values between 0 and 6, so test it out before you use against other DBs.
Here are the limitations:
1. This formula assumes the first day of the week is MONDAY.
2. This formula assumes all days within the same week are CONTINUOUS.
3. This formula will work ONLY when the two dates involved in the calculation falls on a week day or work day, eg. the "Start Date" on a SATURDAY when the location works only MON-FRI will not work.
SELECT
&&START_DATE_YYYYMMDD "Start Date", --in YYYYMMDD format
&&END_DATE_YYYYMMDD "End Date", --in YYYYMMDD format
&&WK_WORK_DAY_CNT "Week Work Day Count", --Number of work day per week
&&DATE_SEQ_NORML_FACTOR "Normalization Factor", --set to 1 when run in Oracle
CASE
WHEN
FLOOR( TO_CHAR( TO_DATE( &&START_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) + &&DATE_SEQ_NORML_FACTOR / 7 ) =
FLOOR( TO_CHAR( TO_DATE( &&END_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) + &&DATE_SEQ_NORML_FACTOR / 7 )
THEN(
TO_CHAR( TO_DATE( &&END_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) -
TO_CHAR( TO_DATE( &&START_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) + 1
)
ELSE(
(
&&WK_WORK_DAY_CNT - MOD( TO_CHAR( TO_DATE( &&START_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) + &&DATE_SEQ_NORML_FACTOR , 7 ) + 1
) +
MOD( TO_CHAR( TO_DATE( &&END_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) + &&DATE_SEQ_NORML_FACTOR , 7 ) +
(
(
(
TO_CHAR( TO_DATE( &&END_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) -
MOD( TO_CHAR( TO_DATE( &&END_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) + &&DATE_SEQ_NORML_FACTOR , 7 )
) -
(
TO_CHAR( TO_DATE( &&START_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) +
(
7 -
(
MOD( TO_CHAR( TO_DATE( &&START_DATE_YYYYMMDD , 'YYYYMMDD') , 'J' ) + &&DATE_SEQ_NORML_FACTOR , 7 )
)
)
)
) / 7 * &&WK_WORK_DAY_CNT
)
) END "Week Day Count"
FROM DUAL
We've incorporated the following logic into several reports with great success. Sorry, I no longer recall the source of this script to give them credit.
DECLARE #range INT;
SET #range = DATEDIFF(DAY, #FirstDate, #SecondDate);
SET #NumWorkDays = (
SELECT
#range / 7 * 5 + #range % 7 - (
SELECT COUNT(*)
FROM (
SELECT 1 AS d
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
UNION ALL SELECT 6
UNION ALL SELECT 7
) weekdays
WHERE d <= #range % 7
AND DATENAME(WEEKDAY, #SecondDate - d) IN ('Saturday', 'Sunday'))
);
I've updated #JOpuckman's function to take into account that different regions don't always have a weekend of Saturday and Sunday. Here's the code in case anyone else needs to apply this globally;
DECLARE #FirstDate DateTime
DECLARE #SecondDate DateTime
SET #FirstDate = '08-20-2012'
SET #SecondDate = '08-24-2012'
DECLARE #range INT;
DECLARE #WeekendDayNameStart VARCHAR(50)
DECLARE #WeekendDayNameEnd VARCHAR(50)
SET #WeekendDayNameStart = 'FRIDAY'
SET #WeekendDayNameEnd = (
SELECT CASE #WeekendDayNameStart
WHEN 'SUNDAY' THEN 'MONDAY'
WHEN 'MONDAY' THEN 'TUESDAY'
WHEN 'TUESDAY' THEN 'WEDNESDAY'
WHEN 'WEDNESDAY' THEN 'THURSDAY'
WHEN 'THURSDAY' THEN 'FRIDAY'
WHEN 'FRIDAY' THEN 'SATURDAY'
WHEN 'SATURDAY' THEN 'SUNDAY'
END
)
DECLARE #NumWorkDays INT
SET #range = DATEDIFF(DAY, #FirstDate, #SecondDate);
SET #NumWorkDays = (
SELECT
#range / 7 * 5 + #range % 7 - (
SELECT COUNT(*)
FROM (
SELECT 1 AS d
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
UNION ALL SELECT 6
UNION ALL SELECT 7
) weekdays
WHERE d <= #range % 7
AND DATENAME(WEEKDAY, #SecondDate - d) IN (#WeekendDayNameStart, #WeekendDayNameEnd))
);
-- Calculate whether the current date is a working day
DECLARE #CurDateExtra INT
SET #CurDateExtra =
(
CASE DATENAME(WEEKDAY, #SecondDate)
WHEN #WeekendDayNameStart THEN 0
WHEN #WeekendDayNameEnd THEN 0
ELSE 1
END
)
SET #NumWorkDays = #NumWorkDays + #CurDateExtra
SELECT #NumWorkDays
Calculates the difference in days between the two dates
Calculates the difference in week numbers and year numbers, subtracts the week numbers and then multiplies the result by 2 to calculate number of non-workdays between the two dates. If year numbers are different calculation of 52 * year difference + week number.
((sysdate - ced.created_dt) + ((((to_char(ced.created_dt,'IW') - ((to_char(sysdate,'YY') - to_char(ced.created_dt,'YY'))* 52))
- to_char(to_char(sysdate,'IW')))) * 2)) duration_in_weekdays
You can try this:
SELECT
Id,
DATEDIFF(d, datefrom, dateto) AS TotDays,
DATEDIFF(wk, datefrom, dateto) AS Wkds,
DATEDIFF(d, datefrom, dateto) - DATEDIFF(wk, datefrom, dateto) AS Days
FROM
YOURTABLE
If your company has a DATES table listing the date and relative work day, as most companies do, you could build a temp table that excludes duplicate relative work days that will also give you the ability to exclude holidays from your calculations as well as weekends.
I have the following query that runs in my Oracle database and I want to have the equivalent for a SQL Server 2008 database:
SELECT TRUNC( /* Midnight Sunday */
NEXT_DAY(SYSDATE, 'SUN') - (7*LEVEL)
) AS week_start,
TRUNC( /* 23:59:59 Saturday */
NEXT_DAY(NEXT_DAY(SYSDATE, 'SUN') - (7*LEVEL), 'SAT') + 1
) - (1/(60*24)) + (59/(60*60*24)) AS week_end
FROM DUAL
CONNECT BY LEVEL <= 4 /* Get the past 4 weeks */
What the query does is get the start of the week and the end of the week for the last 4 weeks. The number of weeks is arbitrary and should be easily modified in the SQL Server query I want. It generates data like the following:
WEEK_START WEEK_END
2010-03-07 00:00:00 2010-03-13 23:59:59
2010-02-28 00:00:00 2010-03-06 23:59:59
...
The part I'm currently stuck on translating is CONNECT BY LEVEL since it seems SQL Server 2008 doesn't have an equivalent. I would prefer to simply adjust a line like CONNECT BY LEVEL <= 4 and have the query generate more or fewer weeks (i.e., I don't want to have to adjust multiple UNION ALL statements).
Edit: here's what I have so far that gets the beginning and end of the current week:
SELECT week_start,
DATEADD(SECOND, -1, DATEADD(DAY, 7, week_start)) AS week_end
FROM (
SELECT CAST(
CONVERT(
VARCHAR(10),
DATEADD(DAY, 1-DATEPART(DW, GETDATE()), GETDATE()),
111
) AS DATETIME
) AS week_start
) AS week_start_view
I don't mind if the query shows the current week start and end date or if it starts at the previous week.
I modified OMG Ponies' answer because it looked like a good idea, it just had the wrong week_end value on each week and also showed future weeks instead of past weeks. I came up with the following:
WITH dates AS (
SELECT DATEADD(
DD,
1 - DATEPART(DW, CONVERT(VARCHAR(10), starting_date, 111)),
CONVERT(VARCHAR(10), starting_date, 111)
) AS midnight
FROM (
SELECT DATEADD(WEEK, -3, GETDATE()) AS starting_date
) AS starting_date_view
UNION ALL
SELECT DATEADD(DD, 7, midnight)
FROM dates
WHERE DATEADD(DD, 7, midnight) < GETDATE()
) SELECT midnight AS week_start,
DATEADD(SS, -1, DATEADD(DAY, 7, midnight)) AS week_end
FROM dates
It generates the past 4 weeks:
week_start week_end
2010-02-14 00:00:00.000 2010-02-20 23:59:59.000
2010-02-21 00:00:00.000 2010-02-27 23:59:59.000
2010-02-28 00:00:00.000 2010-03-06 23:59:59.000
2010-03-07 00:00:00.000 2010-03-13 23:59:59.000
This is better than my previous answer, I think, because it does not rely on another table having a particular number of rows. The number of weeks generated can be changed by altering only one digit: the 3 in SELECT DATEADD(WEEK, -3, GETDATE()) AS starting_date. The current week is included, and that digit represents how many additional weeks prior to the current week should be shown.
Iterate over Past Weeks up to Now, Excluding Current Week
Update: and here's a version that excludes the current week:
WITH dates AS (
SELECT DATEADD(
DD,
1 - DATEPART(DW, CONVERT(VARCHAR(10), starting_date, 111)),
CONVERT(VARCHAR(10), starting_date, 111)
) AS midnight_sunday
FROM (
SELECT DATEADD(WEEK, -4, GETDATE()) AS starting_date
) AS starting_date_view
UNION ALL
SELECT DATEADD(DD, 7, midnight_sunday)
FROM dates
WHERE DATEADD(DD, 7, midnight_sunday) <
DATEADD(
DD,
1 - DATEPART(DW, CONVERT(VARCHAR(10), GETDATE(), 111)),
CONVERT(VARCHAR(10), GETDATE(), 111)
)
) SELECT midnight_sunday AS week_start,
DATEADD(SS, -1, DATEADD(DAY, 7, midnight_sunday)) AS week_end
FROM dates
Its results:
week_start week_end
2010-02-07 00:00:00.000 2010-02-13 23:59:59.000
2010-02-14 00:00:00.000 2010-02-20 23:59:59.000
2010-02-21 00:00:00.000 2010-02-27 23:59:59.000
2010-02-28 00:00:00.000 2010-03-06 23:59:59.000
Iterate over Past Months up to Now
I later found a need for a monthly version of this query. Here is that modification:
WITH dates AS (
SELECT CAST(
FLOOR(CAST(starting_date AS DECIMAL(12, 5))) -
(DAY(starting_date) - 1) AS DATETIME
) AS month_start
FROM (
SELECT DATEADD(MONTH, -3, GETDATE()) AS starting_date
) AS starting_date_view
UNION ALL
SELECT DATEADD(MONTH, 1, month_start)
FROM dates
WHERE DATEADD(MONTH, 1, month_start) < GETDATE()
) SELECT month_start,
DATEADD(SS, -1, DATEADD(MONTH, 1, month_start)) AS month_end
FROM dates
ORDER BY month_start DESC
Use (but don't forget to vote for Sarah):
WITH dates AS (
SELECT DATEADD(DD,
1 - DATEPART(DW, CONVERT(VARCHAR(10), starting_date, 111)),
CONVERT(VARCHAR(10), starting_date, 111)
) AS midnight
FROM (SELECT DATEADD(WEEK, -3, GETDATE()) AS starting_date) AS starting_date_view
UNION ALL
SELECT DATEADD(DD, 7, midnight)
FROM dates
WHERE DATEADD(DD, 7, midnight) < GETDATE())
SELECT midnight AS week_start,
DATEADD(SS, -1, DATEADD(DAY, 7, midnight)) AS week_end
FROM dates
Previously:
Set the first day of the week to be Sunday with the SET DATEFIRST command:
SET DATEFIRST 7
The SQL Server 2005+ equivalent to Oracle's CONNECT BY LEVEL is the recursive CTE (ANSI standard btw). Use:
WITH dates AS (
SELECT DATEADD(DD,
1 - DATEPART(DW, CONVERT(VARCHAR(10), GETDATE(), 111)),
CONVERT(VARCHAR(10), GETDATE(), 111)) AS date
UNION ALL
SELECT DATEADD(dd, 7, d.date)
FROM dates d
WHERE DATEADD(dd, 7, d.date) <= DATEADD(dd, 4*7, GETDATE()))
SELECT t.date AS week_start,
DATEADD(ss, -1, DATEADD(DAY, 7, t.date)) AS week_end
FROM dates t
See this link for explaining how to get the first day of the week. To alter the number of weeks, change the DATEADD(dd, 4*7, GETDATE()), where 4 represents the number of weeks you want generated.
All you need is a set:
;WITH cte(n) AS
(
SELECT 0
UNION ALL SELECT 1
UNION ALL SELECT 2
UNION ALL SELECT 3
)
SELECT week_start,
DATEADD(SECOND, -1, DATEADD(DAY, 7, week_start)) AS week_end
FROM (
SELECT CAST(
CONVERT(
VARCHAR(10),
DATEADD(WEEK, -n, DATEADD(DAY, 1-DATEPART(DW, GETDATE()), GETDATE())),
111
) AS DATETIME
) AS week_start
FROM cte
) AS week_start_view;
However I will caution you that if your data is datetime and you are going to use these boundaries for query ranges, you should use an open-ended range, e.g. >= 03/07 and < 03/14. This way you don't miss out any rows that happened between 23:59:59 and midnight; as rare as they may be, they could be important.
based on your code:
SELECT DATEADD(day, weeks.week * -7, week_start) AS week_start,
DATEADD(SECOND, -1, DATEADD(DAY, (weeks.week-1) * -7, week_start)) AS week_end
FROM (
SELECT CAST(
CONVERT(
VARCHAR(10),
DATEADD(DAY, 1-DATEPART(DW, GETDATE()), GETDATE()),
111
) AS DATETIME
) AS week_start
) AS week_start_view,
( SELECT 0 AS week UNION SELECT 1 UNION SELECT 2 UNION SELECT 3) AS weeks
Here's what I came up with. It's slightly janky in that it relies on at least one of my tables having rows >= the number of weeks I want to select. I could adjust it slightly to include the current week.
SELECT week_start,
DATEADD(SECOND, -1, DATEADD(DAY, 7, week_start)) AS week_end
FROM (
SELECT CAST(
CONVERT(
VARCHAR(10),
DATEADD(
DAY,
1-DATEPART(DW, GETDATE()),
DATEADD(DAY, -7*level, GETDATE())
),
111
) AS DATETIME
) AS week_start
FROM (
SELECT level
FROM (
SELECT ROW_NUMBER() OVER(ORDER BY RAND()) AS level
FROM my_table_with_at_least_21_rows
) AS all_rows_view
WHERE level <= 21
) AS level_view
) AS week_start_view
This gets the past 21 weeks, starting at last week. Here's sample data:
week_start week_end
2010-02-28 00:00:00.000 2010-03-06 23:59:59.000
2010-02-21 00:00:00.000 2010-02-27 23:59:59.000
2010-02-14 00:00:00.000 2010-02-20 23:59:59.000
2010-02-07 00:00:00.000 2010-02-13 23:59:59.000
...