SQL days available using a not exists on current financial year - sql

I have a table called 'holiday table' which basically contains dates for all days where employees will not be in work (e.g bank holidays etc)
The below query is basically looking at the current financial year and working out how many days are available firstly by month, and then using the unuion all cummulatively, (e.g April-May, April-June) I dont need one for April though as I can use the non-cumulative for this.
See query:
DECLARE #StartDate DATETIME,
#EndDate DATETIME
--available days
--current – start of this financial year
SELECT #StartDate = (select
case when month(getdate()) >= 4 then
convert(datetime, cast(year(getdate()) as varchar) + '-4-1')
else
convert(datetime, cast(year(getdate())-1 as varchar) + '-4-1')
end),
--current – end of this financial year
#EndDate = (select
case when month(getdate()) < 4 then
convert(datetime, cast(year(getdate()) as varchar) + '-3-31')
else
convert(datetime, cast(year(getdate())+1 as varchar) + '-3-31')
end)
CREATE TABLE #data
(
firstday DATETIME NOT NULL PRIMARY KEY,
workingdays INT NOT NULL
);
WITH dayscte ([Date])
AS (SELECT #StartDate
UNION ALL
SELECT Dateadd(DAY, 1, [Date])
FROM dayscte
WHERE [Date] <= #Enddate)
INSERT INTO #data
SELECT MIN([Date]),
COUNT(*) [Day]
FROM dayscte
LEFT JOIN dbo.Holiday_Table
ON [Date] BETWEEN dbo.Holiday_Table.sch_cal_d AND dbo.Holiday_Table.sch_cal_ed
where
NOT EXISTS (
SELECT sch_id,sch_cal_d,sch_cal_ed FROM dbo.Holiday_Table WHERE
sch_id ='66291100Ks'
AND
[date] <= sch_cal_d
AND
[date] >= sch_cal_ed
)
AND Datename(weekday, [Date]) NOT IN ( 'Saturday', 'Sunday' )
GROUP BY Datepart(MONTH, [Date]),
Datepart(YEAR, [Date])
OPTION (MAXRECURSION 366)
DECLARE #Date DATETIME
SET #Date = (SELECT MIN(firstday)
FROM #data)
SELECT Period,
workingdays [Days_Available_Minus_Holidays] ,
year (firstday) AS [Year]
FROM (SELECT Datename(MONTH, firstday) [Period],
workingdays,
0 [SortField],
firstday
FROM #data
UNION
SELECT Datename(MONTH, #Date) + '-' + Datename(MONTH, firstday),
(SELECT SUM(workingdays)
FROM #data b
WHERE b.firstday <= a.firstday ) [WorkingDays],
1 [SortField],
firstday
FROM #data a
WHERE
firstday > #Date) data
ORDER BY sortfield,
firstday
DROP TABLE #data
GO
The results for this are as follows:
Period Days_Available_Minus_Holidays Year
April 19 2012
May 22 2012
June 19 2012
July 22 2012
August 22 2012
September 20 2012
October 23 2012
November 22 2012
December 19 2012
January 23 2013
February 20 2013
March 21 2013
April 1 2013
April-May 41 2012
April-June 60 2012
April-July 82 2012
April-August 104 2012
April-September 124 2012
April-October 147 2012
April-November 169 2012
April-December 188 2012
April-January 211 2013
April-February 231 2013
April-March 252 2013
April-April 253 2013
My problem is when I get to the cumulative, it does another 'April' and then at the bottom it does an 'April-April' I do not need a cumulative for April as it is only one month do basically I dont want the first or last cumulative values as April is covered by the non-cumulatives, or if the second 'April' must stay, then it should not read '1' as days available, by be the same as the non-cumulative, which is 19 as this is how many days are actually available.

Try removing the equals in your WITH clause
Change WHERE [Date] <= #Enddate to WHERE [Date] < #Enddate
It seems your adding a day to the date before the WHERE clause therefore it is overstepping by a day.

Related

Get the COUNT(*) records for each month in the year

I have Start_Date(which is '2021-12-01') and End_Date(which is current calendar month)
I need to get COUNT(*) for each month from 2021-12-01 to the current month
Result set should look like this:
December, 2021 - 21
January, 2022 - 44
Feb, 2023 - 11
etc.
So what I tried(I only how do that manually):
DECLARE #datestamp DATE = '12/01/2021'
DECLARE #CurrentDate VARCHAR(100)
SET #CurrentDate = CONVERT(varchar, GETDATE(), 101)
SELECT COUNT(*) AS 'December, 2021'
FROM #TMP
WHERE Start_Date IS NOT NULL AND [Signup_Date] IS NOT NULL
AND Start_Date BETWEEN #datestamp AND '2021-12-31'
But I don't want to copy this query for each of 12 months. How do that dynamically?

how to get the month and year value for the give financial year

how to get the month and year value for the give financial year
if my input is 2013.
my expected out put is
Month Year
4 2013
5 2013
6 2013
7 2013
8 2013
9 2013
10 2013
11 2013
12 2013
1 2014
2 2014
3 2014
Use Recursive CTE to get the expected result.
DECLARE #nYearInput AS CHAR(4)
DECLARE #start DATETIME, #end DATETIME
SET #nYearInput = '2013' -- Change your input here
SET #start = CAST(#nYearInput AS VARCHAR) + '0401'
SET #end= CAST(#nYearInput + 1 AS VARCHAR) + '0301'
;WITH cte AS
(
SELECT dt = DATEADD(DAY, -(DAY(#start) - 1), #start)
UNION ALL
SELECT DATEADD(MONTH, 1, dt)
FROM cte
WHERE dt < DATEADD(DAY, -(DAY(#end) - 1), #end)
)
SELECT YEAR(dt) AS Year, month(dt) AS month
FROM cte

SQL - Create a temp table or CTE of first day of the month and month names

I need to create a temp table or common table expression based on 2 paremters in a SQL Server 2012 environment
#calYear
#currentYear
so if #calYear = 5 and #currentYear='2014'
I would like to generate a temp table of 5 years starting from current year with 4 columns like
YearDesc MonthName MonthNum FirstDayOfMonth
2014 Jan 1 1/1/2014
2014 Feb 2 2/1/2014
2014 Mar 3 3/1/2014
...
...
...
2018 Oct 10 10/1/2018
2018 Nov 11 11/1/2018
2018 Dec 12 12/1/2018
Is it possible to do a Do While loop efficently? How would I account for the month names?
I'm using a really cumbersome Do While loop to iterate all the months of the year then iterate all the years.
One way using a recursive cte:
declare #calYear int = 5, #currentYear char(4) = '2014'
;with cte (dt) as (
select DATEFROMPARTS(#currentyear,1,1) dt
union all
select dateadd(month,1,dt)
from cte where dt < dateadd(year,#calyear,DATEFROMPARTS(#currentyear,1,1))
)
select year(dt) YearDesc, datename(month, dt) MonthName, month(dt) MonthNum, dt FirstDayOfMonth
from cte
order by dt
Or using a numbers table: (in this case master..spt_values)
declare #calYear int = 5, #currentYear char(4) = '2014'
;with cte2 (dt) as (
select dateadd(month,number,DATEFROMPARTS(#currentyear,1,1)) dt
from master..spt_values where type = 'p'
and number <= 12*#calYear
)
select year(dt) YearDesc, datename(month, dt) MonthName, month(dt) MonthNum, dt FirstDayOfMonth
from cte2
order by dt

How to iterate last six month(including current) loop without any custom table?

I want output like follow:
Currently I am using a table. But I don't want to use this with any table(EXCEPT SYSTEM TABLES). Is it possible?
Query(Using a table):
DECLARE #End_date DATETIME
SET #End_Date = DATEADD(month, -6, GETDATE())
SELECT DISTINCT MONTH(S.ACDATE) AS Mon,CONVERT(CHAR(4),S.ACDATE) AS Month_Name,
(YEAR(S.ACDATE) % 100) AS Year_No
FROM SALES as S
WHERE S.ACDATE < DATEADD(month,MONTH(getdate()),
DATEADD(year,YEAR(getdate())-1900,0))
AND S.ACDATE >= DATEADD(month,MONTH(#End_Date)-1,
DATEADD(year,YEAR(#End_Date)-1900,0))
DECLARE #dt DATE = CONVERT(DATE, '05/03/2013', 101)
SELECT MONTH (dt) AS Mon, LEFT (DATENAME (mm, dt), 3) AS Month_Name, YEAR (dt) % 1000 AS Year_No
FROM (
SELECT DATEADD (mm, -diff, #dt) dt
FROM (VALUES(1),(2),(3),(4),(5),(6))t(diff)
) t
Result set is:
Mon Month_Name Year_No
----------- ---------- -----------
4 Apr 13
3 Mar 13
2 Feb 13
1 Jan 13
12 Dec 12
11 Nov 12

SQL Group and Sum By Month - Default to Zero

I am currently grouping and summing inventory usage by month:
SELECT Inventory.itemid AS ItemID,
SUM(Inventory.Totalunits) AS Individual_MonthQty,
MONTH(Inventory.dadded) AS Individual_MonthAsNumber,
DATENAME(MONTH, Inventory.dadded) AS Individual_MonthAsString
FROM Inventory
WHERE Inventory.invtype = 'Shipment'
AND Inventory.dadded >= #StartRange
AND Inventory.dadded <= #EndRange
GROUP BY Inventory.ItemID,
MONTH(Inventory.dadded),
DATENAME(MONTH, Inventory.dadded)
This gives me the results that I'm expecting:
ItemID Kit_MonthQty Kit_MonthAsNumber Kit_MonthAsString
13188 234 8 August
13188 45 9 September
13188 61 10 October
13188 20 12 December
Question
What must I do to return zero for months where no data exsits, like this:
ItemID Kit_MonthQty Kit_MonthAsNumber Kit_MonthAsString
13188 0 1 January
13188 0 2 February
13188 0 3 March
13188 0 4 April
13188 0 5 May
13188 0 6 June
13188 0 7 July
13188 234 8 August
13188 45 9 September
13188 61 10 October
13188 0 11 November
13188 20 12 December
In the past, I've solved a problem like this by creating a temporary table which will hold all dates needed:
CREATE TABLE #AllDates (ThisDate datetime null)
SET #CurrentDate = #StartRange
-- insert all dates into temp table
WHILE #CurrentDate <= #EndRange
BEGIN
INSERT INTO #AllDates values(#CurrentDate)
SET #CurrentDate = dateadd(mm, 1, #CurrentDate)
END
Then, modify your query to join against this table:
SELECT ALLItems.ItemId,
SUM(COALESCE(Inventory.Qty, 0)) AS Individual_MonthQty,
MONTH(#AllDates.ThisDate) AS Individual_MonthAsNumber,
DATENAME(MONTH, #AllDates.ThisDate) AS Individual_MonthAsString
FROM #AllDates
JOIN (SELECT DISTINCT dbo.Inventory.ItemId FROM dbo.Inventory) AS ALLItems ON 1 = 1
LEFT JOIN Inventory ON DATEADD(dd, - DAY(Inventory.dadded) +1, Inventory.dadded) = #AllDates.ThisDate AND ALLItems.ItemId = dbo.Inventory.ItemId
WHERE
#AllDates.ThisDate >= #StartRange
AND #AllDates.ThisDate <= #EndRange
GROUP BY ALLItems.ItemId,
#AllDates.ThisDate
Then you should have a record for each month, regardless of whether it exists in Inventory.
You could prepare a 'calendar' table like so:
DECLARE #d datetime
SET #d = #StartRange
DECLARE #calendar TABLE (Date datetime)
WHILE (#d <= #EndRange) BEGIN
INSERT INTO #Calendar VALUES (#d)
SET #d = DATEADD(month, 1, #d)
END
and then do a LEFT JOIN with your table on the month and year date parts. This way you'll always have all the months between the start and end date as rows.
Here's one way. This assumes that #StartRange and #EndRange fall within the same calendar year, otherwise you will get some funny results.
WITH n(n) AS
(
SELECT TOP 12 ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.objects
),
months(m, mn) AS
(
SELECT n, DATENAME(MONTH, DATEADD(MONTH, m-1, '19000101'))
FROM n
)
SELECT
ItemID = i.itemid,
MonthQty = COALESCE(SUM(TotalUnits), 0),
MonthAsNumber = m.m,
MonthAsString = m.mn
FROM
months AS m
LEFT OUTER JOIN
Inventory AS i
ON
MONTH(i.dadded) = m.m
WHERE
i.invtype = 'Shipment'
AND i.dadded >= #StartRange
AND i.dadded <= #EndRange
GROUP BY
m.m, m.mn, i.itemid
ORDER BY m.m;