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
Wenn Sie dies länger als 1 Jahr tun, müssen Sie die Zeile ändern:
OPTION (MAXRECURSION 366)
Andernfalls erhalten Sie eine Fehlermeldung - Die Zahl muss höher sein als die Anzahl der Tage, die Sie abfragen.
BEARBEITEN
Ich bin gerade auf diese alte Antwort von mir gestoßen und mag sie wirklich nicht, es gibt so viele Dinge, die ich jetzt als schlechte Praxis betrachte, also werde ich alle Probleme korrigieren:
- Ich habe keine Anweisungen mit beendet ein Semikolon richtig
- Benutzte einen rekursiven CTE, um eine Liste von Daten zu generieren
- Die Spaltenliste für eine Einfügung wurde nicht eingefügt
- DATENAME verwendet, um Wochenenden zu eliminieren, was sprachspezifisch ist, viel besser, um explizit
DATEFIRST
zu setzen und verwenden SieDATEPART
- Verwendet
LEFT JOIN/IS NULL
stattNOT EXISTS
Einträge aus der Feiertagstabelle zu entfernen. In SQL Server ist LEFT JOIN/IS NULL weniger effizient als NOT EXISTS
Dies sind alles Kleinigkeiten, aber es sind Dinge, die ich (zumindest in meinem Kopf, wenn nicht sogar laut) kritisieren würde, wenn ich die Anfrage eines anderen überprüfe, also kann ich meine eigene Arbeit nicht wirklich korrigieren! Umschreiben der Abfrage würde ergeben.
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 letzter Punkt wird diese Abfrage viel einfacher mit einem Kalendertabelle die alle Daten speichert und Flags für Arbeitstage, Feiertage usw. hat, anstatt eine Feiertagstabelle zu verwenden, die nur Feiertage speichert.