DECLARE @StartDate DATETIME, @EndDate DATETIME
SELECT @StartDate = '01/04/2011',
@EndDate = '31/03/2012'
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 HolidayTable
ON [Date] BETWEEN HolStart AND HolEnd
WHERE HolidayTypeID IS NULL
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 the Holidays)]
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
Als u dit langer dan 1 jaar doet, moet u de regel wijzigen:
OPTION (MAXRECURSION 366)
Anders krijg je een foutmelding - Het aantal moet hoger zijn dan het aantal dagen dat je opvraagt.
BEWERKEN
Ik ben net dit oude antwoord van mij tegengekomen en vind het echt niet leuk, er zijn zoveel dingen die ik nu als een slechte gewoonte beschouw, dus ik ga alle problemen corrigeren:
- Ik heb verklaringen niet beëindigd met een puntkomma correct
- Een recursieve CTE gebruikt om een lijst met datums te genereren
- Heeft de kolomlijst voor een invoeging niet opgenomen
- DATENAME gebruikt om weekenden te elimineren, wat taalspecifiek is, veel beter om expliciet
DATEFIRST
in te stellen en gebruikDATEPART
- Gebruikt
LEFT JOIN/IS NULL
in plaats vanNOT EXISTS
om records uit de vakantietabel te verwijderen. In SQL Server is LEFT JOIN/IS NULL minder efficiënt dan NOT EXISTS
Dit zijn allemaal kleine dingen, maar het zijn dingen die ik zou bekritiseren (althans in mijn hoofd, zo niet hardop) bij het beoordelen van de vraag van iemand anders, dus ik kan mijn eigen werk niet echt niet corrigeren! Het herschrijven van de query zou opleveren.
SET DATEFIRST 1;
DECLARE @StartDate DATETIME = '20110401',
@EndDate DATETIME = '20120331';
CREATE TABLE #Data (FirstDay DATETIME NOT NULL PRIMARY KEY, WorkingDays INT NOT NULL);
WITH DaysCTE ([Date]) AS
( SELECT TOP (DATEDIFF(DAY, @StartDate, @EndDate) + 1)
DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @StartDate)
FROM sys.all_objects a
)
INSERT INTO #Data (FirstDay, WorkingDays)
SELECT FirstDay = MIN([Date]),
WorkingDays = COUNT(*)
FROM DaysCTE d
WHERE DATEPART(WEEKDAY, [Date]) NOT IN (6, 7)
AND NOT EXISTS
( SELECT 1
FROM dbo.HolidayTable ht
WHERE d.[Date] BETWEEN ht.HolStart AND ht.HolEnd
)
GROUP BY DATEPART(MONTH, [Date]), DATEPART(YEAR, [Date]);
DECLARE @Date DATETIME = (SELECT MIN(FirstDay) FROM #Data);
SELECT Period,
[Days Available (Minus the Holidays)] = WorkingDays
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;
Als laatste punt wordt deze query veel eenvoudiger met een kalendertabel die alle datums opslaat en vlaggen heeft voor werkdagen, vakanties, enz., in plaats van een vakantietabel te gebruiken die alleen vakanties opslaat.