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
Ако правите това повече от 1 година, ще трябва да промените реда:
OPTION (MAXRECURSION 366)
В противен случай ще получите грешка - Числото трябва да е по-голямо от броя на дните, които заявявате.
РЕДАКТИРАНЕ
Току-що попаднах на този мой стар отговор и наистина не ми харесва, има толкова много неща, които сега смятам за лоша практика, така че ще коригирам всички проблеми:
- Не съм прекратил изявления с правилно точка и запетая
- Използван е рекурсивен CTE за генериране на списък с дати
- Не е включен списъкът с колони за вмъкване
- Използва се DATENAME за елиминиране на почивните дни, което е специфично за езика, много по-добре изрично да се зададе
DATEFIRST
и използвайтеDATEPART
- Използван е
LEFT JOIN/IS NULL
вместоNOT EXISTS
за премахване на записи от празничната трапеза. В SQL Server LEFT JOIN/IS NULL е по-малко ефективен от NOT EXISTS
Всичко това са незначителни неща, но те са неща, които бих критикувал (поне в главата си, ако не на глас), когато преглеждам нечие друго запитване, така че наистина не мога да не коригирам собствената си работа! Пренаписването на заявката ще даде.
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;
Като последна точка, тази заявка става много по-проста с календарна таблица която съхранява всички дати и има флагове за работни дни, празници и т.н., вместо да използва празнична таблица, която съхранява само празници.