Need help in understanding on how to Calculate the months by year between 2 dates , able to calculate months between the dates but not by year
ID StartDate Enddate
1 1/1/2016 4/23/2019
2 1/1/2016 4/30/2017
3 1/1/2016 12/31/2018
4 1/1/2017 4/23/2019
5 5/20/2017 11/30/2017
ID StartDate Enddate 2016 2017 2018 2019
1 1/1/2016 4/23/2019 12 12 12 4
2 1/1/2016 4/30/2017 12 4 0 0
3 1/1/2016 12/31/2018 12 12 12 0
4 1/1/2017 4/23/2019 0 12 12 4
5 5/20/2017 11/30/2017 0 7 0 0
Counting intervals like this is tricky in SQL Server. It would be much simpler if the database supported least() and greatest(). Here is a verbose approach:
select id, startdate, enddate,
(case when startdate >= '2017-01-01' or enddate < '2016-01-01' then 0
when startdate < '2016-01-01' and enddate >= '2017-01-01' then 12
when startdate >= '2016-01-01' and enddate >= '2017-01-01' then 13 - month(startdate)
when startdate < '2016-01-01' then month(enddate)
else month(enddate) + 1 - month(startdate)
end) as months_2016,
(case when startdate >= '2018-01-01' or enddate < '2017-01-01' then 0
when startdate < '2017-01-01' and enddate >= '2018-01-01' then 12
when startdate >= '2017-01-01' and enddate >= '2018-01-01' then 13 - month(startdate)
when startdate < '2017-01-01' then month(enddate)
else month(enddate) + 1 - month(startdate)
end) as months_2017,
(case when startdate >= '2019-01-01' or enddate < '2018-01-01' then 0
when startdate < '2018-01-01' and enddate >= '2019-01-01' then 12
when startdate >= '2018-01-01' and enddate >= '2019-01-01' then 13 - month(startdate)
when startdate < '2018-01-01' then month(enddate)
else month(enddate) + 1 - month(startdate)
end) as months_2018
from t;
This basically handles the different conditions on whether the start date is before, during, or after the year in question and the same for the end date.
If you have the years in a table you can do a dynamic pivot like this, it would save you declaring the columns by hand:
CREATE TABLE #Data (ID int, StartDate date, EndDate date);
CREATE TABLE #Year (y int);
SET DATEFORMAT MDY;
insert into #Data
values
(1, '1/1/2016','4/23/2019'),
(2, '1/1/2016','4/30/2017'),
(3, '1/1/2016','12/31/2018'),
(4, '1/1/2017','4/23/2019'),
(5, '5/20/2017','11/30/2017')
;
insert into #Year
values
(2016),
(2017),
(2018),
(2019)
;
DECLARE
#PivotColumnNames AS NVARCHAR(MAX),
#DynamicPivotQuery AS NVARCHAR(MAX);
SELECT
#PivotColumnNames = ISNULL(#PivotColumnNames + ',','') + QUOTENAME(Y)
FROM
#Year;
SELECT
#DynamicPivotQuery =
'SELECT
ID,
StartDate,
EndDate,'
+ #PivotColumnNames +
'FROM
(
select
ID,
StartDate,
EndDate,
Y,
case
when StartDate >= DATEFROMPARTS(Y+1, 1, 1) or EndDate < DATEFROMPARTS(Y, 1, 1) then 0
when StartDate < DATEFROMPARTS(Y, 1, 1) and EndDate >= DATEFROMPARTS(Y+1, 1, 1) then 12
when StartDate >= DATEFROMPARTS(Y, 1, 1) and EndDate >= DATEFROMPARTS(Y+1, 1, 1) then 13 - MONTH(StartDate)
when StartDate < DATEFROMPARTS(Y, 1, 1) then month(EndDate)
else MONTH(EndDate) + 1 - MONTH(StartDate)
end M
from
#Data, #Year
) as SourceTable
PIVOT
(
SUM(M)
FOR Y IN ('
+ #PivotColumnNames +
')
) as PivotTable';
SELECT #DynamicPivotQuery;
EXEC sp_executesql #DynamicPivotQuery;
DROP TABLE #Data, #Year;
Related
The below code when run for the last day of the month it is giving me a week day count of 1 when it should be 0 - how can I fix it?
Declare #EndDate DateTime = '03-31-2021'
;WITH mycte AS (
SELECT #EndDate + 1 DateValue
UNION ALL
SELECT DateValue + 1
FROM mycte
WHERE DateValue < EOMONTH(#EndDate)
)
select
count(case when datepart(dw, DateValue) = 1 then 1 end) SunCount
, count(case when datepart(dw, DateValue) = 7 then 1 end) SatCount
, count(case when datepart(dw, DateValue) between 2 and 6 then 1 end) WeekCount
from mycte
Your initial CTE is actually creating a date of 2021-04-01 which is a week day, so that's where your count of 1 is coming from. If you want to restrict the counts to just the month in question, you could add a WHERE clause to your end query like this. This way, you get zeros for all counts.
Declare #EndDate DateTime = '03-31-2021'
;WITH mycte AS (
SELECT #EndDate + 1 DateValue
UNION ALL
SELECT DateValue +1
FROM mycte
WHERE DateValue < EOMONTH(#EndDate)
)
select
count(case when datepart(dw, DateValue) = 1 then 1 end) SunCount
, count(case when datepart(dw, DateValue) = 7 then 1 end) SatCount
, count(case when datepart(dw, DateValue) between 2 and 6 then 1 end) WeekDayCount
from mycte
where DATEPART(MM,DateValue)=DATEPART(MM,#EndDate)
id date sales
1 01/01/2015 100
2 01/01/2015 100
3 02/01/2015 100
4 03/01/2015 100
What I need is to count the number of sales in a given date range (per month)which is based user input which are StartDate and EndDate. For Example the User inputs StartDate - 01/01/2015 and EndDate - 04/01/2015
the output would be like this
Month StartMonth EndMonth TotalSales
1 01/01/2015 01/31/2015 200
2 02/01/2015 02/28/2015 100
3 03/01/2015 03/31/2015 100
4 04/01/2015 04/30/2015 0
Started to something like this
set #Start_act = cast(DATEADD(month, DATEDIFF(month, 0, #StartDate), 0) as date)
set #End_act = cast(DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, #Enddate)+1, 0)) as date)
set #counter = DATEDIFF(month, #Start_act, #End_act)
if(#counter = 1)
begin
set #counter = #counter
end
else
set #counter = #counter + 1
end
set #count = 0
CREATE TABLE #TempTableID
(
Month int,
StartMonth date,
EndMonth date,
TotalSales
)
while (#count <= #counter)
begin
set #count = #count + 1;
if(#count = 1)
begin
set #Start = #Start_act
set #End = cast(DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, #Start_act)+1, 0)) as date)
set #plannedHorseCapacity = 123
end
else
begin
set #Start = cast(DATEADD(d, 1, #End)as date)
set #End = cast(DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, #Start)+1, 0)) as date)
set #plannedHorseCapacity = 456
end
Insert into #TempTableID
(
Month
StartMonth
EndMonth
TotalSales
)
Values
(
#count,
#Start,
#End,
#TotalSales
)
if(#count > #counter)
begin
break
end
else
begin
continue
end
end
Select * from #TempTableID
You can use below script -
WITH cte
AS
(SELECT
CONVERT(VARCHAR(20), MONTH([date]))
+ '-' + CONVERT(VARCHAR(20), YEAR([date])) monthyear
,([sales])
FROM [sales])
--add where condition for from and to date here
SELECT
monthyear
,SUM(sales) totalsales
FROM cte
GROUP BY monthyear
Output will be
monthyear totalsales
1-2015 400
2-2015 50
3-2015 150
Option 2 With Start and End Date
WITH cte
AS
(SELECT
FORMAT(MONTH([date]), '0#')
+ '-' + CONVERT(VARCHAR(20), YEAR([date])) monthyear
,([sales])
FROM [sales])
--add where condition for from and to date here
SELECT
monthyear
,cast (SUBSTRING(monthyear, 1, 2) as int) [Month]
,SUBSTRING(monthyear, 4, 4) [Year]
,(SELECT
DATEADD(MONTH, SUBSTRING(monthyear, 1, 2) - 1, DATEADD(YEAR, SUBSTRING(monthyear, 4, 4) - 1900, 0)))
StartDate
,(SELECT
DATEADD(DAY, -1, DATEADD(MONTH, CAST(SUBSTRING(monthyear, 1, 2) AS INT), DATEADD(YEAR, SUBSTRING(monthyear, 4, 4) - 1900, 0))))
EndDate
,SUM(sales) totalsales
FROM cte
GROUP BY monthyear
Output will be
monthyear Month Year StartDate EndDate totalsales
01-2015 1 2015 2015-01-01 00:00:00.000 2015-01-31 00:00:00.000 400
02-2015 2 2015 2015-02-01 00:00:00.000 2015-02-28 00:00:00.000 50
03-2015 3 2015 2015-03-01 00:00:00.000 2015-03-31 00:00:00.000 50
11-2015 11 2015 2015-11-01 00:00:00.000 2015-11-30 00:00:00.000 100
EDIT - Sorting
If you have multiple year data, then the dates will not come in sequence to fix that add order by [StartDate] in last.
output will be
monthyear Month Year StartDate EndDate totalsales
01-2015 1 2015 2015-01-01 00:00:00 2015-01-31 00:00:00.000 400
02-2015 2 2015 2015-02-01 00:00:00 2015-02-28 00:00:00.000 50
03-2015 3 2015 2015-03-01 00:00:00 2015-03-31 00:00:00.000 50
11-2015 11 2015 2015-11-01 00:00:00 2015-11-30 00:00:00.000 100
01-2016 1 2016 2016-01-01 00:00:00 2016-01-31 00:00:00.000 125
11-2016 11 2016 2016-11-01 00:00:00 2016-11-30 00:00:00.000 55
You can achieve it like this,
;WITH [CTE_DATE]
AS
(
SELECT MONTH(#fromdt) AS [Month]
,DATEADD(mm, DATEDIFF(mm, 0, #fromdt), 0) AS StartMonth
,DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#fromdt)+1,0)) AS EndMonth
UNION ALL
SELECT [Month] + 1 AS [Month]
,DATEADD(MONTH,1,StartMonth) AS StartMonth
,DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,DATEADD(MONTH,1,StartMonth))+1,0)) AS EndMonth
FROM [CTE_DATE] WHERE [Month] < MONTH(#todt)
)
SELECT *
,(SELECT SUM(sales) FROM yourtable where [date] between StartMonth and EndMonth) as TotalSales
FROM [CTE_DATE]
You can find end of month as below:
select [Month] = Month([date]), [date] as StartMonth,
EndMonth = convert(date, dateadd(month,datediff(month,0,'2017-02-11')+1,0)-1),
TotalSales from (
select [date], sum(sales) as totalSales
from #yoursales
group by [date]
) a
You could use a CTE like this
DECLARE #SampleData AS TABLE
(
id int,
[date] date,
sales int
)
INSERT INTO #SampleData
VALUES
( 1 , '2015-01-01', 100 ),
( 2 , '2015-01-01', 100 ),
( 3 , '2015-02-01', 100 ),
( 4 , '2015-03-01', 100 )
DECLARE #StartDate date = '2015-01-01'
DECLARE #EndDate date = '2015-08-01'
;WITH temp AS -- calendar table
(
SELECT dateadd(month, datediff(month,0,#StartDate),0) AS [StartMonthDate],
dateadd(day, -1 ,dateadd(month, datediff(month,0,#StartDate) + 1,0)) AS [EndMonthDate]
UNION ALL
SELECT dateadd(month, 1, t.[StartMonthDate]),
dateadd(day,-1,dateadd(month, 2, t.[StartMonthDate]))
FROM temp t
WHERE dateadd(month, 1, t.[StartMonthDate]) <= #EndDate
)
SELECT datepart(year,t.StartMonthDate) AS year,
datepart(month,t.StartMonthDate) AS month,
t.StartMonthDate,
t.EndMonthDate,
coalesce(ap.TotalSales,0) AS TotalSales
FROM temp t
CROSS APPLY
(
SELECT SUM(sales) AS TotalSales
FROM #SampleData sd
WHERE sd.[date] BETWEEN t.StartMonthDate AND t.EndMonthDate
) ap
OPTION (MAXRECURSION 0)
Demo link: http://rextester.com/LWUX67185
;With cte(id,date,sales)
AS
(
SELECT 1,'01/01/2015',100 UNION ALL
SELECT 2,'01/01/2015',100 UNION ALL
SELECT 3,'02/01/2015',100 UNION ALL
SELECT 4,'03/01/2015',100 UNION ALL
SELECT 5,'04/01/2015',NULL
)
SELECT [Id],CONVERT(VARCHAR(10),[DATE], 101) AS [DATE],
CONVERT(VARCHAR(10),[EndMonth],101) AS [EndMonth],
[SumOfSale] From
(
SELECT id,[DATE],EndMonth,SUM(sales) OVER(Partition by [DATE],EndMonth order by EndMonth) AS SumOfSale,
Row_NUmber ()OVER(Partition by [DATE],EndMonth order by id)As Seq From
(
SELECT id, CAST([DATE] AS DATE)[DATE], EOMONTH(date)AS EndMonth,ISNULL(sales,0)As Sales from cte
)Dt
)Final
WHERE Final.Seq=1
OutPut
Month StartMonth EndMonth TotalSales
1 01/01/2015 01/31/2015 200
2 02/01/2015 02/28/2015 100
3 03/01/2015 03/31/2015 100
4 04/01/2015 04/30/2015 0
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.
I want to generate based on the day of the week and number of the occurrence in the month of a date, a list of dates for each month between two dates. Assuming I have a #StartDate = 2016/04/01 and #EndDate = 2016/09/01, i check that #StartDate is on a first Friday of April, then to #EndDate will create dates for all first Friday of each month:
2016/05/06
2016/06/03
2016/07/01
2016/08/05
In case #StartDate = 2016/04/12 and #EndDate = 2016/09/01, I note that the #StartDate is the second Tuesday of April, then went to get every second Tuesday Tuesday of each month :
2016/05/10
2016/06/14
2016/07/12
2016/08/09
In case#StartDate = 2016/04/28 and #EndDate = 2016/09/01, I note that the #StartDate is on the last Thursday of the month of April:
2016/05/26
2016/06/30
2016/07/28
2016/08/25
In the last case, i need to verify the number of weeks of each month, because exists months only with 4 weeks or with 5 weeks and i want the last occurrence.
What I have done? I found a code that gives me every Monday in the third week of the month, and i adopted a little to get a #StartDate and #EndDate:
;with
filler as (select row_number() over (order by a) a from (select top 100 1 as a from syscolumns) a cross join (select top 100 1 as b from syscolumns) b),
dates as (select dateadd(month, a-1, #StartDate ) date from filler where a <= 1000 and dateadd(month, a-1, #StartDate) < #EndDate),
FirstMonday as (
select dateadd(day, case datepart(weekday,Date) /*this is the case where verify the week day*/
when 1 then 1
when 2 then 0
when 3 then 6
when 4 then 5
when 5 then 4
when 6 then 3
when 7 then 2
end, Date) as Date
,case when datepart(weekday, #StartDate) = 1 then 3 else 2 end as Weeks /*here i verify the number of weeks to sum in the next date*/
from dates
)
select dateadd(week, Weeks, Date) as ThirdMonday
from FirstMonday
So, it is:
set #NumSemana = datepart(day, datediff(day, DATEADD(mm, DATEDIFF(mm,0,#StartDate), 0), #StartDate)/7 * 7)/7 + 1;
WITH AllDays
AS ( SELECT #StartDate AS [Date], DATEPART(month, #StartDate) as validMonth
UNION ALL
SELECT DATEADD(week, 1, [Date]),
iif(DATEPART(month,DATEADD(week, 1, [Date])) < validMonth + #PeriodicityRepeat, validMonth, validMonth + #PeriodicityRepeat)
FROM AllDays
WHERE
DATEPART(month,[Date]) <= DATEPART(month,#EndDate)
and DATEPART(year,[Date]) <= DATEPART(year,#EndDate)
),
rankedDays
AS(
SELECT [Date], validMonth,
row_number() over ( partition by DATEPART( month, [Date]) order by [Date]) ascOrder,
row_number() over ( partition by DATEPART( month, [Date]) order by [Date] desc) descOrder
FROM AllDays
WHERE DATEPART(month, [Date]) = validMonth
)
select [Date]
from rankedDays
where ((ascOrder = #NumSemana and #NumSemana <=4 )
or (descOrder = 1 and #NumSemana = 5)
or [Date] = #StartDate )
and [Date] < #EndDate
OPTION (MAXRECURSION 0)
Query:
DECLARE #StartDate DATE = '2016-04-28',
#EndDate DATE = '2016-09-01'
;WITH dates AS (
SELECT DATEADD(week, -5, #StartDate) as date_
UNION ALL
SELECT DATEADD(week,1,date_)
FROM dates
WHERE DATEADD(week,1,date_) < #enddate
), final AS (
SELECT ROW_NUMBER() OVER (PARTITION BY DATEPART(year,date_), DATEPART(month,date_) ORDER BY date_ ASC) as RN,
date_
FROM dates
), weeks AS (
SELECT *
FROM (VALUES
(1,1),
(2,2),
(3,3),
(4,4),
(4,5),
(5,4),
(5,5)
) as t(w1,w2)
WHERE w1 = (SELECT RN FROM final WHERE date_ = #StartDate)
)
SELECT MAX(date_) as date_
FROM final f
INNER JOIN weeks w ON f.RN = w.w2
WHERE date_ between #StartDate and #EndDate AND date_ != #StartDate
GROUP BY DATEPART(YEAR,date_), DATEPART(MONTH,date_)
ORDER BY MAX(date_) ASC
Outputs:
For #StartDate = 2016/04/01 and #EndDate = 2016/09/01
date_
----------
2016-05-06
2016-06-03
2016-07-01
2016-08-05
(4 row(s) affected)
For #StartDate = 2016/04/12 and #EndDate = 2016/09/01
date_
----------
2016-05-10
2016-06-14
2016-07-12
2016-08-09
(4 row(s) affected)
For #StartDate = 2016/04/28 and #EndDate = 2016/09/01
date_
----------
2016-05-26
2016-06-30
2016-07-28
2016-08-25
(4 row(s) affected)
I want to calculate which of my holidays are conflict with working days.
I have a Holidays table which is below.
NameOfHoliday StartDate DurationByDay
Christmas 26.12.2015 5
26 and 27 are at weekend. So this shouldt be calculated. So I have to get only 4 days as result.
DECLARE #t TABLE(
NameOfHoliday VARCHAR(10),
StartDate DATE,
DurationByDay SMALLINT
)
INSERT INTO #t
VALUES ('Christmas', '20151226', 5)
;WITH cte AS
(
SELECT *, cnt = 0
FROM #t
UNION ALL
SELECT t2.NameOfHoliday, DATEADD(DAY, t1.cnt + 1, t2.StartDate), t2.DurationByDay, t1.cnt + 1
FROM cte t1
JOIN #t t2 ON t1.NameOfHoliday = t2.NameOfHoliday
WHERE t1.cnt < t1.DurationByDay
)
SELECT NameOfHoliday, StartDate, DT
FROM (
SELECT *
, DT = DATENAME(DW, StartDate)
, RowNum = ROW_NUMBER() OVER (PARTITION BY StartDate ORDER BY NameOfHoliday)
FROM cte
) t
WHERE RowNum = 1
AND DT NOT IN ('Saturday', 'Sunday')
OPTION (MAXRECURSION 0)
output -
NameOfHoliday StartDate DT
------------- ---------- ------------------
Christmas 2015-12-28 Monday
Christmas 2015-12-29 Tuesday
Christmas 2015-12-30 Wednesday
Christmas 2015-12-31 Thursday
I Hope your requirement is like this..
if object_id('tempdb..#Holidays') is not null drop table #Holidays
create table #Holidays(id int identity(1,1),Holiday date)
insert into #Holidays values ('2015-12-25'),('2015-12-28')
set dateformat ymd
declare #StartDate date = '2015-12-01'
declare #EndtDate date = '2015-12-31'
--some times #EndtDate might be NULL so, set to getdate()
set #EndtDate = isnull(#EndtDate,cast(getdate() as date))
declare #Holiday int
set #Holiday = (select count(*) from #Holidays where Holiday between #StartDate and #EndtDate)
SELECT
(DATEDIFF(dd, #StartDate, #EndtDate) + 1)
-(DATEDIFF(wk, #StartDate, #EndtDate) * 2)
-(CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, #EndtDate) = 'Saturday' THEN 1 ELSE 0 END)
-#Holiday